diff --git a/beanie-annotations/src/main/java/com/nosto/beanie/JacksonBean.java b/beanie-annotations/src/main/java/com/nosto/beanie/JacksonBean.java
index cece57f..94d4f17 100644
--- a/beanie-annotations/src/main/java/com/nosto/beanie/JacksonBean.java
+++ b/beanie-annotations/src/main/java/com/nosto/beanie/JacksonBean.java
@@ -10,6 +10,7 @@
package com.nosto.beanie;
+import com.fasterxml.jackson.annotation.JsonCreator;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -19,7 +20,7 @@
* A base class for Jackson-mapped beans.
*
* All sub-classes are automatically unit-tested and they should have
- * a constructor with {@link com.fasterxml.jackson.annotation.JsonCreator}
+ * a constructor with {@link JsonCreator}
* that takes all serialization properties.
*
* Ensures that all beans that extend this can be serialised, then deserialised
diff --git a/beanie-core/src/main/java/com/nosto/beanie/AbstractJacksonBeanTest.java b/beanie-core/src/main/java/com/nosto/beanie/AbstractJacksonBeanTest.java
index fe419cf..d701f62 100644
--- a/beanie-core/src/main/java/com/nosto/beanie/AbstractJacksonBeanTest.java
+++ b/beanie-core/src/main/java/com/nosto/beanie/AbstractJacksonBeanTest.java
@@ -10,38 +10,38 @@
package com.nosto.beanie;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.nosto.beanie.jeasy.ExcludedMapAndCollectionsAsEmptyRandomizerRegistry;
+import com.nosto.beanie.jeasy.ForceAllNonPrimitivesAsNullRandomizerRegistry;
+import org.jeasy.random.EasyRandom;
+import org.jeasy.random.EasyRandomParameters;
+import org.jeasy.random.api.RandomizerRegistry;
+import org.jeasy.random.randomizers.registry.CustomRandomizerRegistry;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import javax.annotation.Nullable;
-
-import org.jeasy.random.EasyRandom;
-import org.jeasy.random.EasyRandomParameters;
-import org.jeasy.random.api.RandomizerRegistry;
-import org.jeasy.random.randomizers.registry.CustomRandomizerRegistry;
-import org.junit.Assert;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.BeanDescription;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.PropertyNamingStrategy;
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
-import com.nosto.beanie.jeasy.ExcludedMapAndCollectionsAsEmptyRandomizerRegistry;
-import com.nosto.beanie.jeasy.ForceAllNonPrimitivesAsNullRandomizerRegistry;
-
@SuppressWarnings("UseOfObsoleteDateTimeApi")
public abstract class AbstractJacksonBeanTest {
@@ -59,12 +59,12 @@ public abstract class AbstractJacksonBeanTest {
private final Class extends T> deserClass;
private final Class extends U> concreteClass;
- @SuppressWarnings({"ConstructorNotProtectedInAbstractClass", "JUnitTestCaseWithNonTrivialConstructors"})
+ @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
public AbstractJacksonBeanTest(Class extends U> clazz) {
this(clazz, clazz);
}
- @SuppressWarnings({"ConstructorNotProtectedInAbstractClass", "JUnitTestCaseWithNonTrivialConstructors"})
+ @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
public AbstractJacksonBeanTest(Class extends T> deserClass, Class extends U> concreteClass) {
this.deserClass = deserClass;
this.concreteClass = concreteClass;
@@ -75,9 +75,7 @@ protected RandomizerRegistry getRandomizerRegistry() {
}
/**
- * Assert all properties have an equivalent
- * {@link com.fasterxml.jackson.annotation.JsonCreator}
- * parameter
+ * Assert all properties have an equivalent {@link JsonCreator} parameter
*/
@Test
public void constructorParameters() {
@@ -117,20 +115,44 @@ public void serde() {
/**
* Test that all properties of a bean are named with a consistent naming strategy.
+ * If the bean is configured to use a specific naming strategy, property names should be consistent with that strategy.
*/
@Test
public void namingStrategy() {
- Map> cases = getDescription().findProperties()
- .stream()
- .map(BeanPropertyDefinition::getName)
- .collect(Collectors.groupingBy(name -> {
- if (name.contains("_") && !name.toLowerCase().equals(name)) {
- return PropertyNamingStrategy.SNAKE_CASE;
+ BeanDescription description = getDescription();
+ getDescription().findProperties()
+ .forEach(property -> verifyPropertyName(description, property));
+ }
+
+ private void verifyPropertyName(BeanDescription bean, BeanPropertyDefinition property) {
+ PropertyNamingStrategy.PropertyNamingStrategyBase namingStrategy = Optional.ofNullable(bean.getClassAnnotations().get(JsonNaming.class))
+ .map(JsonNaming::value)
+ .filter(PropertyNamingStrategy.PropertyNamingStrategyBase.class::isAssignableFrom)
+ .map(c -> {
+ try {
+ return c.getConstructor().newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException("Cannot construct naming strategy.", e);
+ }
+ })
+ .map(PropertyNamingStrategy.PropertyNamingStrategyBase.class::cast)
+ .orElseGet(() -> {
+ // try to detect the naming strategy
+ String name = property.getName();
+ if (name.contains("_")) {
+ return (PropertyNamingStrategy.PropertyNamingStrategyBase) PropertyNamingStrategy.SNAKE_CASE;
} else {
- return PropertyNamingStrategy.LOWER_CAMEL_CASE;
+ return new PropertyNamingStrategy.PropertyNamingStrategyBase() {
+ @Override
+ public String translate(String propertyName) {
+ return propertyName;
+ }
+ };
}
- }));
- Assert.assertEquals(cases.toString(), 1, cases.size());
+ });
+
+ Assert.assertEquals(String.format("Property %s does not use strategy %s", property.getName(), namingStrategy.getClass()),
+ namingStrategy.translate(property.getName()), property.getName());
}
/**
diff --git a/beanie-core/src/main/java/com/nosto/beanie/BeanieProvider.java b/beanie-core/src/main/java/com/nosto/beanie/BeanieProvider.java
index 8fd06b1..4ef647b 100644
--- a/beanie-core/src/main/java/com/nosto/beanie/BeanieProvider.java
+++ b/beanie-core/src/main/java/com/nosto/beanie/BeanieProvider.java
@@ -10,14 +10,14 @@
package com.nosto.beanie;
-import java.io.IOException;
-
-import javax.annotation.Nullable;
-
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
+import javax.annotation.Nullable;
+import java.io.IOException;
+
public interface BeanieProvider {
ObjectMapper getMapper();
@@ -30,7 +30,7 @@ default String toPrettyJSON(@Nullable Object out) {
}
}
- default T fromJSON(String json, Class type) throws IOException {
+ default T fromJSON(String json, Class type) throws JsonProcessingException {
return getMapper().readValue(json, type);
}
diff --git a/beanie-core/src/test/java/com/nosto/beanie/InvalidSnakeCasePropertyNamingStrategyTest.java b/beanie-core/src/test/java/com/nosto/beanie/InvalidSnakeCasePropertyNamingStrategyTest.java
new file mode 100644
index 0000000..60f44b1
--- /dev/null
+++ b/beanie-core/src/test/java/com/nosto/beanie/InvalidSnakeCasePropertyNamingStrategyTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021 Nosto Solutions Ltd All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Nosto Solutions Ltd ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the agreement you entered into with
+ * Nosto Solutions Ltd.
+ */
+
+package com.nosto.beanie;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import org.junit.Test;
+
+public class InvalidSnakeCasePropertyNamingStrategyTest extends AbstractJacksonBeanTest {
+ public InvalidSnakeCasePropertyNamingStrategyTest() {
+ super(TestBean.class);
+ }
+
+ @Test(expected = AssertionError.class)
+ @Override
+ public void namingStrategy() {
+ super.namingStrategy();
+ }
+
+ @Override
+ protected BeanieProvider getBeanieProvider() {
+ return new DefaultBeanieProvider();
+ }
+
+ @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
+ public static class TestBean extends AbstractTestBean {
+
+ private final String propertyA;
+ private final String propertyB;
+
+ @JsonCreator
+ public TestBean(@JsonProperty("propertyA") String propertyA, @JsonProperty("propertyB") String propertyB) {
+ this.propertyA = propertyA;
+ this.propertyB = propertyB;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyA() {
+ return propertyA;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyB() {
+ return propertyB;
+ }
+ }
+}
diff --git a/beanie-core/src/test/java/com/nosto/beanie/PolymorphicJacksonBeanTest.java b/beanie-core/src/test/java/com/nosto/beanie/PolymorphicJacksonBeanTest.java
index 208044e..71bb82f 100644
--- a/beanie-core/src/test/java/com/nosto/beanie/PolymorphicJacksonBeanTest.java
+++ b/beanie-core/src/test/java/com/nosto/beanie/PolymorphicJacksonBeanTest.java
@@ -9,17 +9,11 @@
*/
package com.nosto.beanie;
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.Test;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.JsonTypeName;
+import static org.junit.Assert.assertEquals;
/**
* @author ollik1
@@ -31,7 +25,7 @@ public PolymorphicJacksonBeanTest() {
}
@Test
- public void polymorphicDeserialization() throws IOException {
+ public void polymorphicDeserialization() throws JsonProcessingException {
Base value = getMapper().readValue("{\"type\":\"t1\",\"x\":\"foo\"}", Base.class);
assertEquals(Concrete1.class, value.getClass());
assertEquals("foo", value.getX());
diff --git a/beanie-core/src/test/java/com/nosto/beanie/ValidCamelCasePropertyNamingStrategyTest.java b/beanie-core/src/test/java/com/nosto/beanie/ValidCamelCasePropertyNamingStrategyTest.java
new file mode 100644
index 0000000..8730623
--- /dev/null
+++ b/beanie-core/src/test/java/com/nosto/beanie/ValidCamelCasePropertyNamingStrategyTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021 Nosto Solutions Ltd All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Nosto Solutions Ltd ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the agreement you entered into with
+ * Nosto Solutions Ltd.
+ */
+
+package com.nosto.beanie;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class ValidCamelCasePropertyNamingStrategyTest extends AbstractJacksonBeanTest {
+ public ValidCamelCasePropertyNamingStrategyTest() {
+ super(TestBean.class);
+ }
+
+ @Override
+ protected BeanieProvider getBeanieProvider() {
+ return new DefaultBeanieProvider();
+ }
+
+ public static class TestBean extends AbstractTestBean {
+
+ private final String propertyA;
+ private final String propertyB;
+
+ @JsonCreator
+ public TestBean(@JsonProperty("propertyA") String propertyA, @JsonProperty("propertyB") String propertyB) {
+ this.propertyA = propertyA;
+ this.propertyB = propertyB;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyA() {
+ return propertyA;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyB() {
+ return propertyB;
+ }
+ }
+}
diff --git a/beanie-core/src/test/java/com/nosto/beanie/ValidSnakeCasePropertyNamingStrategyTest.java b/beanie-core/src/test/java/com/nosto/beanie/ValidSnakeCasePropertyNamingStrategyTest.java
new file mode 100644
index 0000000..aa0f0bc
--- /dev/null
+++ b/beanie-core/src/test/java/com/nosto/beanie/ValidSnakeCasePropertyNamingStrategyTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021 Nosto Solutions Ltd All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Nosto Solutions Ltd ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the agreement you entered into with
+ * Nosto Solutions Ltd.
+ */
+
+package com.nosto.beanie;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+
+public class ValidSnakeCasePropertyNamingStrategyTest extends AbstractJacksonBeanTest {
+ public ValidSnakeCasePropertyNamingStrategyTest() {
+ super(TestBean.class);
+ }
+
+ @Override
+ protected BeanieProvider getBeanieProvider() {
+ return new DefaultBeanieProvider();
+ }
+
+ @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
+ public static class TestBean extends AbstractTestBean {
+
+ private final String propertyA;
+ private final String propertyB;
+
+ @JsonCreator
+ public TestBean(@JsonProperty("property_a") String propertyA, @JsonProperty("property_b") String propertyB) {
+ this.propertyA = propertyA;
+ this.propertyB = propertyB;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyA() {
+ return propertyA;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPropertyB() {
+ return propertyB;
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
index 724932c..a4456d7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -32,7 +32,7 @@ allprojects { project ->
apply plugin: "jacoco"
group 'com.nosto'
- version '1.0.5'
+ version '1.0.6'
repositories {
mavenCentral()
@@ -168,29 +168,42 @@ allprojects { project ->
pom {
//noinspection GroovyAssignabilityCheck
name = 'Beanie'
+ //noinspection GroovyAssignabilityCheck
description = 'A simple library to sanity-check your bean ser-deser'
+ //noinspection GroovyAssignabilityCheck
url = 'https://github.com/nosto/beanie'
licenses {
license {
+ //noinspection GroovyAssignabilityCheck
name = 'The Apache License, Version 2.0'
+ //noinspection GroovyAssignabilityCheck
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
+ //noinspection GroovyAssignabilityCheck
id = 'ollik1'
+ //noinspection GroovyAssignabilityCheck
name = 'Olli Kuonanoja'
+ //noinspection GroovyAssignabilityCheck
email = 'olli@nosto.com'
}
developer {
+ //noinspection GroovyAssignabilityCheck
id = 'mridang'
+ //noinspection GroovyAssignabilityCheck
name = 'Mridang Agarwalla'
+ //noinspection GroovyAssignabilityCheck
email = 'mridang@nosto.com'
}
}
scm {
+ //noinspection GroovyAssignabilityCheck
connection = 'scm:git:git://github.com/nosto/beanie.git'
+ //noinspection GroovyAssignabilityCheck
developerConnection = 'scm:git:ssh://github.com/nosto/beanie.git'
+ //noinspection GroovyAssignabilityCheck
url = 'https://github.com/nosto/beanie'
}
}