From dbd76b7e79431c4f3bbc2bd56c5be4cf54c6da9d Mon Sep 17 00:00:00 2001 From: lpandzic Date: Thu, 15 Apr 2021 14:31:40 +0200 Subject: [PATCH 1/9] added SingleArgumentPropertiesCreatorModeAnnotationIntrospector --- .../infobip/jackson/InfobipJacksonModule.java | 3 +- ...tiesCreatorModeAnnotationIntrospector.java | 49 +++++++++++++++++++ ...ustomTypeFieldSimpleJsonHierarchyTest.java | 4 +- ...sonTypedAbstractClassDeserializerTest.java | 5 +- .../jackson/JsonTypedDeserializerTest.java | 5 +- .../LowerCaseTypeSimpleJsonHierarchyTest.java | 5 +- ...ltiHierarchyJsonTypedDeserializerTest.java | 4 +- ...OneTypeFieldJsonTypedDeserializerTest.java | 4 +- ...entPropertyCaseFormatDeserializerTest.java | 3 -- .../PresentPropertyDeserializerTest.java | 3 -- ...rtyResolverWithDefaultConstructorTest.java | 3 -- .../SimpleJsonHierarchyDeserializerTest.java | 5 +- 12 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java diff --git a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/InfobipJacksonModule.java b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/InfobipJacksonModule.java index eebcd22..d25bf5c 100644 --- a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/InfobipJacksonModule.java +++ b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/InfobipJacksonModule.java @@ -8,5 +8,6 @@ public class InfobipJacksonModule extends SimpleModule { public void setupModule(SetupContext context) { super.setupModule(context); context.insertAnnotationIntrospector(new InfobipJacksonAnnotationIntrospector()); + context.insertAnnotationIntrospector(new SingleArgumentPropertiesCreatorModeAnnotationIntrospector()); } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java new file mode 100644 index 0000000..45822fa --- /dev/null +++ b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java @@ -0,0 +1,49 @@ +package com.infobip.jackson; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.*; + +import java.lang.reflect.Field; +import java.util.Objects; +import java.util.stream.Stream; + +public class SingleArgumentPropertiesCreatorModeAnnotationIntrospector extends NopAnnotationIntrospector { + + @Override + public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { + JsonCreator ann = _findAnnotation(a, JsonCreator.class); + + if (Objects.nonNull(ann)) { + return ann.mode(); + } + + if (!(a instanceof AnnotatedConstructor)) { + return null; + } + + AnnotatedConstructor annotatedConstructor = (AnnotatedConstructor) a; + Class declaringClass = annotatedConstructor.getDeclaringClass(); + if (Throwable.class.isAssignableFrom(declaringClass)) { + return null; + } + + if (annotatedConstructor.getParameterCount() != 1) { + return null; + } + + if (declaringClass.getDeclaredConstructors().length > 1) { + return null; + } + + String parameterName = annotatedConstructor.getAnnotated().getParameters()[0].getName(); + boolean hasAMatchingField = Stream.of(declaringClass.getDeclaredFields()) + .map(Field::getName) + .anyMatch(fieldName -> fieldName.equals(parameterName)); + if(!hasAMatchingField) { + return null; + } + + return JsonCreator.Mode.PROPERTIES; + } +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/CustomTypeFieldSimpleJsonHierarchyTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/CustomTypeFieldSimpleJsonHierarchyTest.java index 77039d1..350a648 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/CustomTypeFieldSimpleJsonHierarchyTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/CustomTypeFieldSimpleJsonHierarchyTest.java @@ -110,14 +110,12 @@ interface FooBar extends SimpleJsonHierarchy { FooBarType getType(); } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Foo implements FooBar { private final String foo; private final FooBarType type = FooBarType.FOO; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bar implements FooBar { private final String bar; @@ -132,4 +130,4 @@ enum FooBarType implements TypeProvider { private final Class type; } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedAbstractClassDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedAbstractClassDeserializerTest.java index be25be6..e722226 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedAbstractClassDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedAbstractClassDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.*; import org.junit.jupiter.api.Test; @@ -48,14 +47,12 @@ public Class resolve(Map json) { } } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Foo extends FooBar { private final String foo; private final FooBarType type = FooBarType.FOO; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bar extends FooBar { private final String bar; @@ -70,4 +67,4 @@ enum FooBarType { private final Class type; } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedDeserializerTest.java index 75c2e22..b7d4d5b 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/JsonTypedDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; @@ -113,14 +112,12 @@ public Class resolve(Map json) { } } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Foo implements FooBar { private final String foo; private final FooBarType type = FooBarType.FOO; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bar implements FooBar { private final String bar; @@ -135,4 +132,4 @@ enum FooBarType { private final Class type; } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/LowerCaseTypeSimpleJsonHierarchyTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/LowerCaseTypeSimpleJsonHierarchyTest.java index f9e9d7d..7b3bd8b 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/LowerCaseTypeSimpleJsonHierarchyTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/LowerCaseTypeSimpleJsonHierarchyTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -108,14 +107,12 @@ interface FooBar extends SimpleJsonHierarchy { FooBarType getType(); } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Foo implements FooBar { private final String foo; private final FooBarType type = FooBarType.FOO; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bar implements FooBar { private final String bar; @@ -135,4 +132,4 @@ String getValue() { return toString().toLowerCase(); } } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyJsonTypedDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyJsonTypedDeserializerTest.java index 144c81f..46fb526 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyJsonTypedDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyJsonTypedDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; @@ -123,7 +122,6 @@ public MammalJsonTypeResolver() { } } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Human implements Mammal { private final String name; @@ -146,4 +144,4 @@ enum MammalType implements TypeProvider { private final Class type; } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyWithOneTypeFieldJsonTypedDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyWithOneTypeFieldJsonTypedDeserializerTest.java index f436f3d..76d0e3b 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyWithOneTypeFieldJsonTypedDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/MultiHierarchyWithOneTypeFieldJsonTypedDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; @@ -106,7 +105,6 @@ interface Animal extends SimpleJsonHierarchy { interface Mammal extends Animal { } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Human implements Mammal { private final String name; @@ -124,4 +122,4 @@ enum AnimalType implements TypeProvider { private final Class type; } -} \ No newline at end of file +} diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyCaseFormatDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyCaseFormatDeserializerTest.java index 7ac5563..dfd6ddb 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyCaseFormatDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyCaseFormatDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.PropertyNamingStrategy; @@ -117,14 +116,12 @@ public LowerUnderscorePresentPropertyJsonTypeResolver(Class type) { } } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class RoadBike implements Bike { private final String roadBike; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class MountainBike implements Bike { diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyDeserializerTest.java index ba7eb9f..0a3c62c 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; @@ -103,14 +102,12 @@ interface Bike extends PresentPropertyJsonHierarchy { } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class RoadBike implements Bike { private final String roadBike; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bmx implements Bike { diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyResolverWithDefaultConstructorTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyResolverWithDefaultConstructorTest.java index 22a39ff..2afbad0 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyResolverWithDefaultConstructorTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyResolverWithDefaultConstructorTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.CaseFormat; import lombok.*; @@ -43,14 +42,12 @@ public BikeTypeResolver() { } } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class RoadBike implements Bike { private final String roadBike; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class MountainBike implements Bike { diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SimpleJsonHierarchyDeserializerTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SimpleJsonHierarchyDeserializerTest.java index fe08905..ea87744 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SimpleJsonHierarchyDeserializerTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SimpleJsonHierarchyDeserializerTest.java @@ -1,6 +1,5 @@ package com.infobip.jackson; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; @@ -105,14 +104,12 @@ void shouldDeserializeFooAsFooFromJson() throws JsonProcessingException { interface FooBar extends SimpleJsonHierarchy { } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Foo implements FooBar { private final String foo; private final FooBarType type = FooBarType.FOO; } - @AllArgsConstructor(onConstructor_ = @JsonCreator) @Value static class Bar implements FooBar { private final String bar; @@ -127,4 +124,4 @@ enum FooBarType implements TypeProvider { private final Class type; } -} \ No newline at end of file +} From c3b253bb885f39c6489ba3943ca937d525d2ca07 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 08:25:20 +0200 Subject: [PATCH 2/9] added SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest upgraded spring dependencies --- ...tiesCreatorModeAnnotationIntrospector.java | 13 +- ...CreatorModeAnnotationIntrospectorTest.java | 139 ++++++++++++++++++ pom.xml | 2 +- 3 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java diff --git a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java index 45822fa..f85ea60 100644 --- a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java +++ b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java @@ -5,33 +5,24 @@ import com.fasterxml.jackson.databind.introspect.*; import java.lang.reflect.Field; -import java.util.Objects; import java.util.stream.Stream; public class SingleArgumentPropertiesCreatorModeAnnotationIntrospector extends NopAnnotationIntrospector { @Override public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { - JsonCreator ann = _findAnnotation(a, JsonCreator.class); - - if (Objects.nonNull(ann)) { - return ann.mode(); - } - if (!(a instanceof AnnotatedConstructor)) { return null; } AnnotatedConstructor annotatedConstructor = (AnnotatedConstructor) a; - Class declaringClass = annotatedConstructor.getDeclaringClass(); - if (Throwable.class.isAssignableFrom(declaringClass)) { - return null; - } if (annotatedConstructor.getParameterCount() != 1) { return null; } + Class declaringClass = annotatedConstructor.getDeclaringClass(); + if (declaringClass.getDeclaredConstructors().length > 1) { return null; } diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java new file mode 100644 index 0000000..c18b872 --- /dev/null +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java @@ -0,0 +1,139 @@ +package com.infobip.jackson; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import lombok.*; +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.BDDAssertions.then; + +class SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest extends TestBase { + + @Test + void shouldDeserializeClassWithSingleFieldAndOnlySingleParameterConstructor() throws JsonProcessingException { + // given + String givenJson = "{'foo':'givenFoo'}"; + + // when + ClassWithSingleFieldAndOnlySingleParameterConstructor actual = objectMapper.readValue(givenJson, + ClassWithSingleFieldAndOnlySingleParameterConstructor.class); + + // then + then(actual).isEqualTo(new ClassWithSingleFieldAndOnlySingleParameterConstructor("givenFoo")); + } + + @Test + void shouldDeserializeClassWithMultipleFieldsAndOnlySingleParameterConstructor() throws + JsonProcessingException { + // given + String givenJson = "{'foo':'givenFoo'}"; + + // when + ClassWithMultipleFieldsAndOnlySingleParameterConstructor actual = objectMapper.readValue(givenJson, + ClassWithMultipleFieldsAndOnlySingleParameterConstructor.class); + + // then + then(actual).isEqualTo(new ClassWithMultipleFieldsAndOnlySingleParameterConstructor("givenFoo")); + } + + @Test + void shouldDeserializeClassWithDelegatingCreatorConstructor() throws + JsonProcessingException { + // given + String givenJson = "{'foo':'givenFoo'}"; + + // when + ClassWithMultipleFieldsAndOnlySingleParameterConstructor actual = objectMapper.readValue(givenJson, + ClassWithMultipleFieldsAndOnlySingleParameterConstructor.class); + + // then + then(actual).isEqualTo(new ClassWithMultipleFieldsAndOnlySingleParameterConstructor("givenFoo")); + } + + @Test + void shouldFailToDeserializeClassWithMultipleConstructors() { + // given + String givenJson = "{'foo':'givenFoo'}"; + + // when + Throwable actual = BDDAssertions.catchThrowable( + () -> objectMapper.readValue(givenJson, ClassWithMultipleConstructors.class)); + + // then + then(actual).isInstanceOf(MismatchedInputException.class) + .hasMessage("Cannot construct instance of `com.infobip.jackson.SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest$ClassWithMultipleConstructors` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n" + + " at [Source: (String)\"{'foo':'givenFoo'}\"; line: 1, column: 2]"); + } + + @Test + void shouldFailToDeserializeClassWithMismatchingParameterName() { + // given + String givenJson = "{'foo':'givenFooBar'}"; + + // when + Throwable actual = BDDAssertions.catchThrowable(() -> objectMapper.readValue(givenJson, + ClassWithMismatchingParameterName.class)); + + // then + then(actual).isInstanceOf(MismatchedInputException.class) + .hasMessage( + "Cannot construct instance of `com.infobip.jackson.SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest$ClassWithMismatchingParameterName` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n" + + " at [Source: (String)\"{'foo':'givenFooBar'}\"; line: 1, column: 2]"); + } + + @Value + static class ClassWithSingleFieldAndOnlySingleParameterConstructor { + + private final String foo; + + public ClassWithSingleFieldAndOnlySingleParameterConstructor(String foo) { + this.foo = foo; + } + } + + @Value + static class ClassWithMultipleFieldsAndOnlySingleParameterConstructor { + + private final String foo; + private final String bar = "bar"; + } + + @Value + static class ClassWithDelegatingCreatorConstructor { + + private final String foo; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public ClassWithDelegatingCreatorConstructor(Map foo) { + this.foo = foo.get("foo"); + } + } + + @ToString + @EqualsAndHashCode + static class ClassWithMultipleConstructors { + + private final String foo; + + public ClassWithMultipleConstructors(String foo) { + this.foo = foo; + } + + public ClassWithMultipleConstructors(Integer bar) { + this.foo = null; + } + } + + static class ClassWithMismatchingParameterName { + + private final String foo; + + public ClassWithMismatchingParameterName(String bar) { + this.foo = bar; + } + } +} diff --git a/pom.xml b/pom.xml index f66039e..0221d70 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 2.3.2.RELEASE + 2.4.4.RELEASE 3.7.0 From 9ed7c3f5b563b76d306565172d3ab0425a0fa925 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 08:40:53 +0200 Subject: [PATCH 3/9] added additional tests --- ...tiesCreatorModeAnnotationIntrospector.java | 26 ++++++++--- ...CreatorModeAnnotationIntrospectorTest.java | 44 ++++++++++++++++++- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java index f85ea60..bd1987d 100644 --- a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java +++ b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java @@ -1,10 +1,12 @@ package com.infobip.jackson; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.*; import java.lang.reflect.Field; +import java.util.Objects; import java.util.stream.Stream; public class SingleArgumentPropertiesCreatorModeAnnotationIntrospector extends NopAnnotationIntrospector { @@ -27,14 +29,28 @@ public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated return null; } - String parameterName = annotatedConstructor.getAnnotated().getParameters()[0].getName(); - boolean hasAMatchingField = Stream.of(declaringClass.getDeclaredFields()) - .map(Field::getName) - .anyMatch(fieldName -> fieldName.equals(parameterName)); - if(!hasAMatchingField) { + if (doesntHaveMatchingFieldAndParameter(annotatedConstructor, declaringClass)) { return null; } return JsonCreator.Mode.PROPERTIES; } + + private boolean doesntHaveMatchingFieldAndParameter(AnnotatedConstructor annotatedConstructor, + Class declaringClass) { + String parameterName = annotatedConstructor.getAnnotated().getParameters()[0].getName(); + return Stream.of(declaringClass.getDeclaredFields()) + .noneMatch(field -> doesFieldMatchParameter(field, parameterName)); + } + + private boolean doesFieldMatchParameter(Field field, String parameterName) { + + JsonProperty annotation = field.getAnnotation(JsonProperty.class); + + if (Objects.nonNull(annotation) && annotation.value().equals(parameterName)) { + return true; + } + + return field.getName().equals(parameterName); + } } diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java index c18b872..29884f3 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java @@ -1,6 +1,7 @@ package com.infobip.jackson; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import lombok.*; @@ -26,6 +27,31 @@ void shouldDeserializeClassWithSingleFieldAndOnlySingleParameterConstructor() th then(actual).isEqualTo(new ClassWithSingleFieldAndOnlySingleParameterConstructor("givenFoo")); } + @Test + void shouldDeserializeClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor() throws JsonProcessingException { + // given + String givenJson = "{'bar':'givenBar'}"; + + // when + ClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor actual = objectMapper.readValue(givenJson, + ClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor.class); + + // then + then(actual).isEqualTo(new ClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor("givenBar")); + } + + @Test + void shouldDeserializeClassWithMultipleProperties() throws JsonProcessingException { + // given + String givenJson = "{'foo':'givenFoo', 'bar':'givenBar'}"; + + // when + ClassWithMultipleProperties actual = objectMapper.readValue(givenJson, ClassWithMultipleProperties.class); + + // then + then(actual).isEqualTo(new ClassWithMultipleProperties("givenFoo", "givenBar")); + } + @Test void shouldDeserializeClassWithMultipleFieldsAndOnlySingleParameterConstructor() throws JsonProcessingException { @@ -89,12 +115,26 @@ void shouldFailToDeserializeClassWithMismatchingParameterName() { static class ClassWithSingleFieldAndOnlySingleParameterConstructor { private final String foo; + } - public ClassWithSingleFieldAndOnlySingleParameterConstructor(String foo) { - this.foo = foo; + @Value + static class ClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor { + + @JsonProperty("bar") + private final String foo; + + public ClassWithSingleFieldAnnotatedWithJsonPropertyAndOnlySingleParameterConstructor(String bar) { + this.foo = bar; } } + @Value + static class ClassWithMultipleProperties { + + private final String foo; + private final String bar; + } + @Value static class ClassWithMultipleFieldsAndOnlySingleParameterConstructor { From 38d7907adfa9393ab4c7d170b2f990fa2e1d3d1e Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 08:42:34 +0200 Subject: [PATCH 4/9] removed .RELEASE suffix from new spring boot version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0221d70..55f2a82 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 2.4.4.RELEASE + 2.4.4 3.7.0 From 1d0bbee42d717100fe6c3bfee616b7c93f708809 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 08:43:05 +0200 Subject: [PATCH 5/9] changed repository id --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55f2a82..2a6a1b9 100644 --- a/pom.xml +++ b/pom.xml @@ -177,7 +177,7 @@ - ossrh-releases + ossrh SonatypeReleases https://oss.sonatype.org/service/local/staging/deploy/maven2 From 49e3d2d23104435f6323c08069deea77c42b3924 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 09:19:50 +0200 Subject: [PATCH 6/9] updated README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 74fab4a..c823554 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Library which provides new features for (de)serialization on top of [Jackson lib * [Multi level hierarchies](#MultiLevelHierarchies) * [Parallel hierarchies](#ParallelHierarchies) * [Typeless (present property)](#Typeless) + * [Single Argument Property Creator annotationless support](#SingleArgumentPropertyCreatorAnnotationlessSupport) ## Setup @@ -245,6 +246,24 @@ Notice standard jackson `@JsonNaming` annotation in `Bike` interface. [Showcase](infobip-jackson-extension-module/src/test/java/com/infobip/jackson/PresentPropertyCaseFormatDeserializerTest.java). + +### Single Argument Property Creator annotationless support + +This module also adds support for deserializing single property value objects when using parameter names module: + +```java +class Foo { + private final Bar bar; + + Foo(Bar bar) { + this.bar = bar; + } +} +``` + +without any additional configuration or annotations. +Related issues: https://github.com/FasterXML/jackson-databind/issues/1498, https://github.com/spring-projects/spring-boot/issues/26023. + ## Requirements: - Java 8 From 00f4005249014450218d5c150ac0daff5fdc6635 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 09:43:20 +0200 Subject: [PATCH 7/9] added support for detecting mismatching parameter and field type --- ...tiesCreatorModeAnnotationIntrospector.java | 12 +++++++--- ...CreatorModeAnnotationIntrospectorTest.java | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java index bd1987d..d934700 100644 --- a/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java +++ b/infobip-jackson-extension-module/src/main/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospector.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.introspect.*; import java.lang.reflect.Field; +import java.lang.reflect.Parameter; import java.util.Objects; import java.util.stream.Stream; @@ -38,13 +39,18 @@ public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated private boolean doesntHaveMatchingFieldAndParameter(AnnotatedConstructor annotatedConstructor, Class declaringClass) { - String parameterName = annotatedConstructor.getAnnotated().getParameters()[0].getName(); + Parameter parameter = annotatedConstructor.getAnnotated().getParameters()[0]; return Stream.of(declaringClass.getDeclaredFields()) - .noneMatch(field -> doesFieldMatchParameter(field, parameterName)); + .noneMatch(field -> doesFieldMatchParameter(field, parameter)); } - private boolean doesFieldMatchParameter(Field field, String parameterName) { + private boolean doesFieldMatchParameter(Field field, Parameter parameter) { + if(!field.getType().equals(parameter.getType())) { + return false; + } + + String parameterName = parameter.getName(); JsonProperty annotation = field.getAnnotation(JsonProperty.class); if (Objects.nonNull(annotation) && annotation.value().equals(parameterName)) { diff --git a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java index 29884f3..3872f5b 100644 --- a/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java +++ b/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java @@ -95,6 +95,21 @@ void shouldFailToDeserializeClassWithMultipleConstructors() { " at [Source: (String)\"{'foo':'givenFoo'}\"; line: 1, column: 2]"); } + @Test + void shouldFailToDeserializeClassWithMismatchingParameterType() { + // given + String givenJson = "{'foo':1}"; + + // when + Throwable actual = BDDAssertions.catchThrowable( + () -> objectMapper.readValue(givenJson, ClassWithMismatchingParameterType.class)); + + // then + then(actual).isInstanceOf(MismatchedInputException.class) + .hasMessage("Cannot construct instance of `com.infobip.jackson.SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest$ClassWithMismatchingParameterType` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n" + + " at [Source: (String)\"{'foo':1}\"; line: 1, column: 2]"); + } + @Test void shouldFailToDeserializeClassWithMismatchingParameterName() { // given @@ -168,6 +183,15 @@ public ClassWithMultipleConstructors(Integer bar) { } } + static class ClassWithMismatchingParameterType { + + private final String foo; + + public ClassWithMismatchingParameterType(Integer foo) { + this.foo = foo.toString(); + } + } + static class ClassWithMismatchingParameterName { private final String foo; From c0541329fd7b70e7c768938893fc64187248378a Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 09:48:09 +0200 Subject: [PATCH 8/9] added snyk badge to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c823554..d5f125f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ![](https://github.com/infobip/infobip-jackson-extension/workflows/maven/badge.svg) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.infobip/infobip-jackson-extension-spring-boot-starter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.infobip/infobip-jackson-extension-spring-boot-starter) [![Coverage Status](https://coveralls.io/repos/github/infobip/infobip-jackson-extension/badge.svg?branch=master)](https://coveralls.io/github/infobip/infobip-jackson-extension?branch=master) +[![Known Vulnerabilities](https://snyk.io/test/github/infobip/infobip-jackson-extension/badge.svg)](https://snyk.io/test/github/infobip/infobip-jackson-extension) Library which provides new features for (de)serialization on top of [Jackson library](https://github.com/FasterXML/jackson). From 4aba117b4019f36ca6cf4655b0844f4762e31186 Mon Sep 17 00:00:00 2001 From: lpandzic Date: Fri, 16 Apr 2021 09:51:21 +0200 Subject: [PATCH 9/9] bumped guava to latest version --- infobip-jackson-extension-api/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infobip-jackson-extension-api/pom.xml b/infobip-jackson-extension-api/pom.xml index c195b62..a5ec33c 100644 --- a/infobip-jackson-extension-api/pom.xml +++ b/infobip-jackson-extension-api/pom.xml @@ -13,8 +13,8 @@ com.google.guava guava - 29.0-jre + 30.1.1-jre infobip-jackson-extension-api - \ No newline at end of file +