From 5b065941d68c08682b80134f97add609056362ee Mon Sep 17 00:00:00 2001 From: sunyijun Date: Fri, 11 Dec 2020 10:25:06 +0800 Subject: [PATCH 1/3] Fix collectAll() throw conflicting in POJOPropertiesCollector when having namingStrategy. swap _renameUsing and trimByVisibility. in _renameUsing will check conflict, but trimByVisibility will filter some conflict, so trimByVisibility before _renameUsing. --- .../databind/introspect/POJOPropertiesCollector.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java index 525e0fd129..94428fb67a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -403,18 +403,18 @@ protected void collectAll() property.mergeAnnotations(_forSerialization); } - // And use custom naming strategy, if applicable... - PropertyNamingStrategy naming = _findNamingStrategy(); - if (naming != null) { - _renameUsing(props, naming); - } - // Sort by visibility (explicit over implicit); drop all but first of member // type (getter, setter etc) if there is visibility difference for (POJOPropertyBuilder property : props.values()) { property.trimByVisibility(); } + // And use custom naming strategy, if applicable... + PropertyNamingStrategy naming = _findNamingStrategy(); + if (naming != null) { + _renameUsing(props, naming); + } + // and, if required, apply wrapper name: note, MUST be done after // annotations are merged. if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) { From 3f4552752ea0177e9bd5c6b59c88efebaa11fed3 Mon Sep 17 00:00:00 2001 From: sunyijun Date: Fri, 11 Dec 2020 13:50:52 +0800 Subject: [PATCH 2/3] add test cases for conflict setter --- .../POJOPropertiesCollectorTest.java | 1144 +++++++++-------- 1 file changed, 619 insertions(+), 525 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java index 4bb7aa1aa5..18d1250283 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java @@ -5,231 +5,325 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.math.BigDecimal; -import java.util.*; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JacksonAnnotation; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; public class POJOPropertiesCollectorTest - extends BaseMapTest -{ - static class Simple { - public int value; - - @JsonProperty("value") - public void valueSetter(int v) { value = v; } - - @JsonProperty("value") - public int getFoobar() { return value; } - } - - static class SimpleFieldDeser - { - @JsonDeserialize String[] values; - } - - static class SimpleGetterVisibility { - public int getA() { return 0; } - protected int getB() { return 1; } - @SuppressWarnings("unused") - private int getC() { return 2; } - } - - // Class for testing 'shared ignore' - static class Empty { - public int value; - - public void setValue(int v) { value = v; } - - @JsonIgnore - public int getValue() { return value; } - } - - static class IgnoredSetter { - @JsonProperty - public int value; - - @JsonIgnore - public void setValue(int v) { value = v; } - - public int getValue() { return value; } - } - - static class ImplicitIgnores { - @JsonIgnore public int a; - @JsonIgnore public void setB(int b) { } - public int c; - } - - // Should find just one setter for "y", due to partial ignore - static class IgnoredRenamedSetter { - @JsonIgnore public void setY(int value) { } - @JsonProperty("y") void foobar(int value) { } - } - - // should produce a single property, "x" - static class RenamedProperties { - @JsonProperty("x") - public int value; - - public void setValue(int v) { value = v; } - - public int getX() { return value; } - } - - static class RenamedProperties2 - { - @JsonProperty("renamed") - public int getValue() { return 1; } - public void setValue(int x) { } - } - - // Testing that we can "merge" properties with renaming - static class MergedProperties { - public int x; - - @JsonProperty("x") - public void setFoobar(int v) { x = v; } - } - - // Testing that property order is obeyed, even for deserialization purposes - @JsonPropertyOrder({"a", "b", "c", "d"}) - static class SortedProperties - { - public int b; - public int c; - - public void setD(int value) { } - public void setA(int value) { } - } - - // [JACKSON-700]: test property type detection, selection - static class TypeTestBean - { - protected Long value; - - @JsonCreator - public TypeTestBean(@JsonProperty("value") String value) { } - - // If you remove this method, the test will pass - public Integer getValue() { return 0; } - } - - static class Jackson703 - { - private List location = new ArrayList(); - - { - location.add(new FoodOrgLocation()); - } - - public List getLocation() { return location; } - } - - static class FoodOrgLocation - { - protected Long id; - public String name; - protected Location location; - - public FoodOrgLocation() { - location = new Location(); - } - - public FoodOrgLocation(final Location foodOrg) { } - - public FoodOrgLocation(final Long id, final String name, final Location location) { } - - public Location getLocation() { return location; } - } - - static class Location { - public BigDecimal lattitude; - public BigDecimal longitude; - - public Location() { } - - public Location(final BigDecimal lattitude, final BigDecimal longitude) { } - } - - class Issue701Bean { // important: non-static! - private int i; - - // annotation does not matter -- just need one on the last argument - public Issue701Bean(@JsonProperty int i) { this.i = i; } - - public int getX() { return i; } - } - - static class Issue744Bean - { - protected Map additionalProperties; - - @JsonAnySetter - public void addAdditionalProperty(String key, Object value) { - if (additionalProperties == null) additionalProperties = new HashMap(); - additionalProperties.put(key,value); - } - - public void setAdditionalProperties(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - @JsonAnyGetter - public Map getAdditionalProperties() { return additionalProperties; } - - @JsonIgnore - public String getName() { - return (String) additionalProperties.get("name"); - } - } - - static class PropDescBean - { - public final static String A_DESC = "That's A!"; - public final static int B_INDEX = 3; - - @JsonPropertyDescription(A_DESC) - public String a; - - protected int b; - - public String getA() { return a; } - - public void setA(String a) { this.a = a; } - - @JsonProperty(required=true, index=B_INDEX, defaultValue="13") - public int getB() { return b; } - } - - @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - @Retention(RetentionPolicy.RUNTIME) - @JacksonAnnotation - @interface A {} - - @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - @Retention(RetentionPolicy.RUNTIME) - @JacksonAnnotation - @interface B {} - - static class DuplicateGetterBean - { - @A - public boolean isBloop() { return true; } - - @B - public boolean getBloop() { return true; } - } - - static class DuplicateGetterCreatorBean - { - public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) {} - - public boolean isBloop() { return true; } - - public boolean getBloop() { return true; } - } + extends BaseMapTest { + static class Simple { + public int value; + + @JsonProperty("value") + public void valueSetter(int v) { + value = v; + } + + @JsonProperty("value") + public int getFoobar() { + return value; + } + } + + static class SimpleFieldDeser { + @JsonDeserialize + String[] values; + } + + static class SimpleGetterVisibility { + public int getA() { + return 0; + } + + protected int getB() { + return 1; + } + + @SuppressWarnings("unused") + private int getC() { + return 2; + } + } + + // Class for testing 'shared ignore' + static class Empty { + public int value; + + public void setValue(int v) { + value = v; + } + + @JsonIgnore + public int getValue() { + return value; + } + } + + static class IgnoredSetter { + @JsonProperty + public int value; + + @JsonIgnore + public void setValue(int v) { + value = v; + } + + public int getValue() { + return value; + } + } + + static class ImplicitIgnores { + @JsonIgnore + public int a; + + @JsonIgnore + public void setB(int b) { + } + + public int c; + } + + // Should find just one setter for "y", due to partial ignore + static class IgnoredRenamedSetter { + @JsonIgnore + public void setY(int value) { + } + + @JsonProperty("y") + void foobar(int value) { + } + } + + // should produce a single property, "x" + static class RenamedProperties { + @JsonProperty("x") + public int value; + + public void setValue(int v) { + value = v; + } + + public int getX() { + return value; + } + } + + static class RenamedProperties2 { + @JsonProperty("renamed") + public int getValue() { + return 1; + } + + public void setValue(int x) { + } + } + + // Testing that we can "merge" properties with renaming + static class MergedProperties { + public int x; + + @JsonProperty("x") + public void setFoobar(int v) { + x = v; + } + } + + // Testing that property order is obeyed, even for deserialization purposes + @JsonPropertyOrder({"a", "b", "c", "d"}) + static class SortedProperties { + public int b; + public int c; + + public void setD(int value) { + } + + public void setA(int value) { + } + } + + // [JACKSON-700]: test property type detection, selection + static class TypeTestBean { + protected Long value; + + @JsonCreator + public TypeTestBean(@JsonProperty("value") String value) { + } + + // If you remove this method, the test will pass + public Integer getValue() { + return 0; + } + } + + static class Jackson703 { + private List location = new ArrayList(); + + { + location.add(new FoodOrgLocation()); + } + + public List getLocation() { + return location; + } + } + + static class FoodOrgLocation { + protected Long id; + public String name; + protected Location location; + + public FoodOrgLocation() { + location = new Location(); + } + + public FoodOrgLocation(final Location foodOrg) { + } + + public FoodOrgLocation(final Long id, final String name, final Location location) { + } + + public Location getLocation() { + return location; + } + } + + static class Location { + public BigDecimal lattitude; + public BigDecimal longitude; + + public Location() { + } + + public Location(final BigDecimal lattitude, final BigDecimal longitude) { + } + } + + class Issue701Bean { // important: non-static! + private int i; + + // annotation does not matter -- just need one on the last argument + public Issue701Bean(@JsonProperty int i) { + this.i = i; + } + + public int getX() { + return i; + } + } + + static class Issue744Bean { + protected Map additionalProperties; + + @JsonAnySetter + public void addAdditionalProperty(String key, Object value) { + if (additionalProperties == null) + additionalProperties = new HashMap(); + additionalProperties.put(key, value); + } + + public void setAdditionalProperties(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + @JsonIgnore + public String getName() { + return (String)additionalProperties.get("name"); + } + } + + static class PropDescBean { + public final static String A_DESC = "That's A!"; + public final static int B_INDEX = 3; + + @JsonPropertyDescription(A_DESC) + public String a; + + protected int b; + + public String getA() { + return a; + } + + public void setA(String a) { + this.a = a; + } + + @JsonProperty(required = true, index = B_INDEX, defaultValue = "13") + public int getB() { + return b; + } + } + + @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @JacksonAnnotation + @interface A { + } + + @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @JacksonAnnotation + @interface B { + } + + static class DuplicateGetterBean { + @A + public boolean isBloop() { + return true; + } + + @B + public boolean getBloop() { + return true; + } + } + + static class DuplicateGetterCreatorBean { + public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) { + } + + public boolean isBloop() { + return true; + } + + public boolean getBloop() { + return true; + } + } + + static class DuplicateSetterBean { + public void setBloop(Boolean bloop) { + } + + @JsonSetter + public void setBloop(Object bloop) { + } + } /* /********************************************************** @@ -237,313 +331,313 @@ public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) {} /********************************************************** */ - private final ObjectMapper MAPPER = objectMapper(); - - public void testSimple() - { - POJOPropertiesCollector coll = collector(MAPPER, - Simple.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("value"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleFieldVisibility() - { - // false -> deserialization - POJOPropertiesCollector coll = collector(MAPPER, - SimpleFieldDeser.class, false); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("values"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleGetterVisibility() - { - POJOPropertiesCollector coll = collector(MAPPER, - SimpleGetterVisibility.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("a"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - // Unit test for verifying that a single @JsonIgnore can remove the - // whole property, unless explicit property marker exists - public void testEmpty() - { - POJOPropertiesCollector coll = collector(MAPPER, - Empty.class, true); - Map props = coll.getPropertyMap(); - assertEquals(0, props.size()); - } - - // Unit test for verifying handling of 'partial' @JsonIgnore; that is, - // if there is at least one explicit annotation to indicate property, - // only parts that are ignored are, well, ignored - public void testPartialIgnore() - { - POJOPropertiesCollector coll = collector(MAPPER, - IgnoredSetter.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("value"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleRenamed() - { - POJOPropertiesCollector coll = collector(MAPPER, - RenamedProperties.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("x"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleRenamed2() - { - POJOPropertiesCollector coll = collector(MAPPER, - RenamedProperties2.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("renamed"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - public void testMergeWithRename() - { - POJOPropertiesCollector coll = collector(MAPPER, - MergedProperties.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("x"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleIgnoreAndRename() - { - POJOPropertiesCollector coll = collector(MAPPER, - IgnoredRenamedSetter.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("y"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - public void testGlobalVisibilityForGetters() - { - ObjectMapper m = jsonMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) - .build(); - POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true); - // should be 1, expect that we disabled getter auto-detection, so - Map props = coll.getPropertyMap(); - assertEquals(0, props.size()); - } - - public void testCollectionOfIgnored() - { - POJOPropertiesCollector coll = collector(MAPPER, ImplicitIgnores.class, false); - // should be 1, due to ignorals - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - // but also have 2 ignored properties - Collection ign = coll.getIgnoredPropertyNames(); - assertEquals(2, ign.size()); - assertTrue(ign.contains("a")); - assertTrue(ign.contains("b")); - } - - public void testSimpleOrderingForDeserialization() - { - POJOPropertiesCollector coll = collector(MAPPER, SortedProperties.class, false); - List props = coll.getProperties(); - assertEquals(4, props.size()); - assertEquals("a", props.get(0).getName()); - assertEquals("b", props.get(1).getName()); - assertEquals("c", props.get(2).getName()); - assertEquals("d", props.get(3).getName()); - } - - public void testSimpleWithType() - { - // first for serialization; should base choice on getter - POJOPropertiesCollector coll = collector(MAPPER, TypeTestBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - assertEquals("value", props.get(0).getName()); - AnnotatedMember m = props.get(0).getAccessor(); - assertTrue(m instanceof AnnotatedMethod); - assertEquals(Integer.class, m.getRawType()); - - // then for deserialization; prefer ctor param - coll = collector(MAPPER, TypeTestBean.class, false); - props = coll.getProperties(); - assertEquals(1, props.size()); - assertEquals("value", props.get(0).getName()); - m = props.get(0).getMutator(); - assertEquals(AnnotatedParameter.class, m.getClass()); - assertEquals(String.class, m.getRawType()); - } - - public void testInnerClassWithAnnotationsInCreator() throws Exception - { - BeanDescription beanDesc; - // first with serialization - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); - assertNotNull(beanDesc); - // then with deserialization - beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); - assertNotNull(beanDesc); - } - - public void testUseAnnotationsFalse() throws Exception - { - // note: need a separate mapper, need to reconfigure - ObjectMapper mapper = jsonMapperBuilder() - .configure(MapperFeature.USE_ANNOTATIONS, false) - .build(); - BeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class)); - assertNotNull(beanDesc); - - Jackson703 bean = new Jackson703(); - String json = mapper.writeValueAsString(bean); - assertNotNull(json); - } - - public void testJackson744() throws Exception - { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect - (MAPPER.constructType(Issue744Bean.class)); - assertNotNull(beanDesc); - AnnotatedMember setter = beanDesc.findAnySetterAccessor(); - assertNotNull(setter); - assertEquals("addAdditionalProperty", setter.getName()); - assertTrue(setter instanceof AnnotatedMethod); - } - - // [databind#269]: Support new @JsonPropertyDescription - public void testPropertyDesc() throws Exception - { - // start via deser - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, true, false, "13"); - // and then via ser: - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, true, false, "13"); - } - - // [databind#438]: Support @JsonProperty.index - public void testPropertyIndex() throws Exception - { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, false, true, "13"); - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, false, true, "13"); - } - - public void testDuplicateGetters() throws Exception - { - POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - BeanPropertyDefinition prop = props.get(0); - assertEquals("bloop", prop.getName()); - assertTrue(prop.getGetter().hasAnnotation(A.class)); - assertTrue(prop.getGetter().hasAnnotation(B.class)); - } - - public void testDuplicateGettersCreator() throws Exception - { - POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterCreatorBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = (POJOPropertyBuilder) props.get(0); - assertEquals("bloop", prop.getName()); - // Can't call getGetter or the duplicate will be removed - assertTrue(prop._getters.value.hasAnnotation(A.class)); - assertNotNull(prop._getters.next); - assertTrue(prop._getters.next.value.hasAnnotation(A.class)); - } - - private void _verifyProperty(BeanDescription beanDesc, - boolean verifyDesc, boolean verifyIndex, String expDefaultValue) - { - assertNotNull(beanDesc); - List props = beanDesc.findProperties(); - assertEquals(2, props.size()); - for (BeanPropertyDefinition prop : props) { - String name = prop.getName(); - final PropertyMetadata md = prop.getMetadata(); - if ("a".equals(name)) { - assertFalse(md.isRequired()); - assertNull(md.getRequired()); - if (verifyDesc) { - assertEquals(PropDescBean.A_DESC, md.getDescription()); - } - if (verifyIndex) { - assertNull(md.getIndex()); - } - } else if ("b".equals(name)) { - assertTrue(md.isRequired()); - assertEquals(Boolean.TRUE, md.getRequired()); - if (verifyDesc) { - assertNull(md.getDescription()); - } - if (verifyIndex) { - assertEquals(Integer.valueOf(PropDescBean.B_INDEX), md.getIndex()); - } - if (expDefaultValue != null) { - assertEquals(expDefaultValue, md.getDefaultValue()); - } - } else { - fail("Unrecognized property '"+name+"'"); - } - } - } + private final ObjectMapper MAPPER = objectMapper(); + + public void testSimple() { + POJOPropertiesCollector coll = collector(MAPPER, + Simple.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("value"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleFieldVisibility() { + // false -> deserialization + POJOPropertiesCollector coll = collector(MAPPER, + SimpleFieldDeser.class, false); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("values"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleGetterVisibility() { + POJOPropertiesCollector coll = collector(MAPPER, + SimpleGetterVisibility.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("a"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + // Unit test for verifying that a single @JsonIgnore can remove the + // whole property, unless explicit property marker exists + public void testEmpty() { + POJOPropertiesCollector coll = collector(MAPPER, + Empty.class, true); + Map props = coll.getPropertyMap(); + assertEquals(0, props.size()); + } + + // Unit test for verifying handling of 'partial' @JsonIgnore; that is, + // if there is at least one explicit annotation to indicate property, + // only parts that are ignored are, well, ignored + public void testPartialIgnore() { + POJOPropertiesCollector coll = collector(MAPPER, + IgnoredSetter.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("value"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleRenamed() { + POJOPropertiesCollector coll = collector(MAPPER, + RenamedProperties.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("x"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleRenamed2() { + POJOPropertiesCollector coll = collector(MAPPER, + RenamedProperties2.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("renamed"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + public void testMergeWithRename() { + POJOPropertiesCollector coll = collector(MAPPER, + MergedProperties.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("x"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleIgnoreAndRename() { + POJOPropertiesCollector coll = collector(MAPPER, + IgnoredRenamedSetter.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("y"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + public void testGlobalVisibilityForGetters() { + ObjectMapper m = jsonMapperBuilder() + .configure(MapperFeature.AUTO_DETECT_GETTERS, false) + .build(); + POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true); + // should be 1, expect that we disabled getter auto-detection, so + Map props = coll.getPropertyMap(); + assertEquals(0, props.size()); + } + + public void testCollectionOfIgnored() { + POJOPropertiesCollector coll = collector(MAPPER, ImplicitIgnores.class, false); + // should be 1, due to ignorals + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + // but also have 2 ignored properties + Collection ign = coll.getIgnoredPropertyNames(); + assertEquals(2, ign.size()); + assertTrue(ign.contains("a")); + assertTrue(ign.contains("b")); + } + + public void testSimpleOrderingForDeserialization() { + POJOPropertiesCollector coll = collector(MAPPER, SortedProperties.class, false); + List props = coll.getProperties(); + assertEquals(4, props.size()); + assertEquals("a", props.get(0).getName()); + assertEquals("b", props.get(1).getName()); + assertEquals("c", props.get(2).getName()); + assertEquals("d", props.get(3).getName()); + } + + public void testSimpleWithType() { + // first for serialization; should base choice on getter + POJOPropertiesCollector coll = collector(MAPPER, TypeTestBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + assertEquals("value", props.get(0).getName()); + AnnotatedMember m = props.get(0).getAccessor(); + assertTrue(m instanceof AnnotatedMethod); + assertEquals(Integer.class, m.getRawType()); + + // then for deserialization; prefer ctor param + coll = collector(MAPPER, TypeTestBean.class, false); + props = coll.getProperties(); + assertEquals(1, props.size()); + assertEquals("value", props.get(0).getName()); + m = props.get(0).getMutator(); + assertEquals(AnnotatedParameter.class, m.getClass()); + assertEquals(String.class, m.getRawType()); + } + + public void testInnerClassWithAnnotationsInCreator() throws Exception { + BeanDescription beanDesc; + // first with serialization + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + assertNotNull(beanDesc); + // then with deserialization + beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + assertNotNull(beanDesc); + } + + public void testUseAnnotationsFalse() throws Exception { + // note: need a separate mapper, need to reconfigure + ObjectMapper mapper = jsonMapperBuilder() + .configure(MapperFeature.USE_ANNOTATIONS, false) + .build(); + BeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class)); + assertNotNull(beanDesc); + + Jackson703 bean = new Jackson703(); + String json = mapper.writeValueAsString(bean); + assertNotNull(json); + } + + public void testJackson744() throws Exception { + BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect + (MAPPER.constructType(Issue744Bean.class)); + assertNotNull(beanDesc); + AnnotatedMember setter = beanDesc.findAnySetterAccessor(); + assertNotNull(setter); + assertEquals("addAdditionalProperty", setter.getName()); + assertTrue(setter instanceof AnnotatedMethod); + } + + // [databind#269]: Support new @JsonPropertyDescription + public void testPropertyDesc() throws Exception { + // start via deser + BeanDescription beanDesc = MAPPER.getDeserializationConfig() + .introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, true, false, "13"); + // and then via ser: + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, true, false, "13"); + } + + // [databind#438]: Support @JsonProperty.index + public void testPropertyIndex() throws Exception { + BeanDescription beanDesc = MAPPER.getDeserializationConfig() + .introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, false, true, "13"); + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, false, true, "13"); + } + + public void testDuplicateGetters() throws Exception { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertTrue(prop.getGetter().hasAnnotation(A.class)); + assertTrue(prop.getGetter().hasAnnotation(B.class)); + } + + public void testDuplicateGettersCreator() throws Exception { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterCreatorBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = (POJOPropertyBuilder)props.get(0); + assertEquals("bloop", prop.getName()); + // Can't call getGetter or the duplicate will be removed + assertTrue(prop._getters.value.hasAnnotation(A.class)); + assertNotNull(prop._getters.next); + assertTrue(prop._getters.next.value.hasAnnotation(A.class)); + } + + public void testDuplicateSetters() throws Exception { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateSetterBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertEquals(prop.getSetter().getRawParameterType(0), Object.class); + } + + public void testDuplicateSettersHaveCustomNamingStrategy() throws Exception { + ObjectMapper mapper = newJsonMapper(); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE); + POJOPropertiesCollector coll = collector(mapper, DuplicateSetterBean.class, false); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertEquals(prop.getSetter().getRawParameterType(0), Object.class); + } + + private void _verifyProperty(BeanDescription beanDesc, + boolean verifyDesc, boolean verifyIndex, String expDefaultValue) { + assertNotNull(beanDesc); + List props = beanDesc.findProperties(); + assertEquals(2, props.size()); + for (BeanPropertyDefinition prop : props) { + String name = prop.getName(); + final PropertyMetadata md = prop.getMetadata(); + if ("a".equals(name)) { + assertFalse(md.isRequired()); + assertNull(md.getRequired()); + if (verifyDesc) { + assertEquals(PropDescBean.A_DESC, md.getDescription()); + } + if (verifyIndex) { + assertNull(md.getIndex()); + } + } else if ("b".equals(name)) { + assertTrue(md.isRequired()); + assertEquals(Boolean.TRUE, md.getRequired()); + if (verifyDesc) { + assertNull(md.getDescription()); + } + if (verifyIndex) { + assertEquals(Integer.valueOf(PropDescBean.B_INDEX), md.getIndex()); + } + if (expDefaultValue != null) { + assertEquals(expDefaultValue, md.getDefaultValue()); + } + } else { + fail("Unrecognized property '" + name + "'"); + } + } + } /* /********************************************************** /* Helper methods /********************************************************** */ - protected POJOPropertiesCollector collector(ObjectMapper m0, - Class cls, boolean forSerialization) - { - BasicClassIntrospector bci = new BasicClassIntrospector(); - // no real difference between serialization, deserialization, at least here - if (forSerialization) { - return bci.collectProperties(m0.getSerializationConfig(), - m0.constructType(cls), null, true); - } - return bci.collectProperties(m0.getDeserializationConfig(), - m0.constructType(cls), null, false); - } + protected POJOPropertiesCollector collector(ObjectMapper m0, + Class cls, boolean forSerialization) { + BasicClassIntrospector bci = new BasicClassIntrospector(); + // no real difference between serialization, deserialization, at least here + if (forSerialization) { + return bci.collectProperties(m0.getSerializationConfig(), + m0.constructType(cls), null, true); + } + return bci.collectProperties(m0.getDeserializationConfig(), + m0.constructType(cls), null, false); + } } From 91358cc7eeff87cbed7adfa1ca9e79a98f11c59a Mon Sep 17 00:00:00 2001 From: sunyijun Date: Thu, 17 Dec 2020 10:14:31 +0800 Subject: [PATCH 3/3] revert format --- .../POJOPropertiesCollectorTest.java | 1173 ++++++++--------- 1 file changed, 554 insertions(+), 619 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java index 18d1250283..e4082afcc2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java @@ -5,325 +5,240 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JacksonAnnotation; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.databind.BaseMapTest; -import com.fasterxml.jackson.databind.BeanDescription; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyMetadata; -import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import java.util.*; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; public class POJOPropertiesCollectorTest - extends BaseMapTest { - static class Simple { - public int value; - - @JsonProperty("value") - public void valueSetter(int v) { - value = v; - } - - @JsonProperty("value") - public int getFoobar() { - return value; - } - } - - static class SimpleFieldDeser { - @JsonDeserialize - String[] values; - } - - static class SimpleGetterVisibility { - public int getA() { - return 0; - } - - protected int getB() { - return 1; - } - - @SuppressWarnings("unused") - private int getC() { - return 2; - } - } - - // Class for testing 'shared ignore' - static class Empty { - public int value; - - public void setValue(int v) { - value = v; - } - - @JsonIgnore - public int getValue() { - return value; - } - } - - static class IgnoredSetter { - @JsonProperty - public int value; - - @JsonIgnore - public void setValue(int v) { - value = v; - } - - public int getValue() { - return value; - } - } - - static class ImplicitIgnores { - @JsonIgnore - public int a; - - @JsonIgnore - public void setB(int b) { - } - - public int c; - } - - // Should find just one setter for "y", due to partial ignore - static class IgnoredRenamedSetter { - @JsonIgnore - public void setY(int value) { - } - - @JsonProperty("y") - void foobar(int value) { - } - } - - // should produce a single property, "x" - static class RenamedProperties { - @JsonProperty("x") - public int value; - - public void setValue(int v) { - value = v; - } - - public int getX() { - return value; - } - } - - static class RenamedProperties2 { - @JsonProperty("renamed") - public int getValue() { - return 1; - } - - public void setValue(int x) { - } - } - - // Testing that we can "merge" properties with renaming - static class MergedProperties { - public int x; - - @JsonProperty("x") - public void setFoobar(int v) { - x = v; - } - } - - // Testing that property order is obeyed, even for deserialization purposes - @JsonPropertyOrder({"a", "b", "c", "d"}) - static class SortedProperties { - public int b; - public int c; - - public void setD(int value) { - } - - public void setA(int value) { - } - } - - // [JACKSON-700]: test property type detection, selection - static class TypeTestBean { - protected Long value; - - @JsonCreator - public TypeTestBean(@JsonProperty("value") String value) { - } - - // If you remove this method, the test will pass - public Integer getValue() { - return 0; - } - } - - static class Jackson703 { - private List location = new ArrayList(); - - { - location.add(new FoodOrgLocation()); - } - - public List getLocation() { - return location; - } - } - - static class FoodOrgLocation { - protected Long id; - public String name; - protected Location location; - - public FoodOrgLocation() { - location = new Location(); - } - - public FoodOrgLocation(final Location foodOrg) { - } - - public FoodOrgLocation(final Long id, final String name, final Location location) { - } - - public Location getLocation() { - return location; - } - } - - static class Location { - public BigDecimal lattitude; - public BigDecimal longitude; - - public Location() { - } - - public Location(final BigDecimal lattitude, final BigDecimal longitude) { - } - } - - class Issue701Bean { // important: non-static! - private int i; - - // annotation does not matter -- just need one on the last argument - public Issue701Bean(@JsonProperty int i) { - this.i = i; - } - - public int getX() { - return i; - } - } - - static class Issue744Bean { - protected Map additionalProperties; - - @JsonAnySetter - public void addAdditionalProperty(String key, Object value) { - if (additionalProperties == null) - additionalProperties = new HashMap(); - additionalProperties.put(key, value); - } - - public void setAdditionalProperties(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return additionalProperties; - } - - @JsonIgnore - public String getName() { - return (String)additionalProperties.get("name"); - } - } - - static class PropDescBean { - public final static String A_DESC = "That's A!"; - public final static int B_INDEX = 3; - - @JsonPropertyDescription(A_DESC) - public String a; - - protected int b; - - public String getA() { - return a; - } - - public void setA(String a) { - this.a = a; - } - - @JsonProperty(required = true, index = B_INDEX, defaultValue = "13") - public int getB() { - return b; - } - } - - @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - @Retention(RetentionPolicy.RUNTIME) - @JacksonAnnotation - @interface A { - } - - @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - @Retention(RetentionPolicy.RUNTIME) - @JacksonAnnotation - @interface B { - } - - static class DuplicateGetterBean { - @A - public boolean isBloop() { - return true; - } - - @B - public boolean getBloop() { - return true; - } - } - - static class DuplicateGetterCreatorBean { - public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) { - } - - public boolean isBloop() { - return true; - } - - public boolean getBloop() { - return true; - } - } - - static class DuplicateSetterBean { - public void setBloop(Boolean bloop) { - } - - @JsonSetter - public void setBloop(Object bloop) { - } - } + extends BaseMapTest +{ + static class Simple { + public int value; + + @JsonProperty("value") + public void valueSetter(int v) { value = v; } + + @JsonProperty("value") + public int getFoobar() { return value; } + } + + static class SimpleFieldDeser + { + @JsonDeserialize String[] values; + } + + static class SimpleGetterVisibility { + public int getA() { return 0; } + protected int getB() { return 1; } + @SuppressWarnings("unused") + private int getC() { return 2; } + } + + // Class for testing 'shared ignore' + static class Empty { + public int value; + + public void setValue(int v) { value = v; } + + @JsonIgnore + public int getValue() { return value; } + } + + static class IgnoredSetter { + @JsonProperty + public int value; + + @JsonIgnore + public void setValue(int v) { value = v; } + + public int getValue() { return value; } + } + + static class ImplicitIgnores { + @JsonIgnore public int a; + @JsonIgnore public void setB(int b) { } + public int c; + } + + // Should find just one setter for "y", due to partial ignore + static class IgnoredRenamedSetter { + @JsonIgnore public void setY(int value) { } + @JsonProperty("y") void foobar(int value) { } + } + + // should produce a single property, "x" + static class RenamedProperties { + @JsonProperty("x") + public int value; + + public void setValue(int v) { value = v; } + + public int getX() { return value; } + } + + static class RenamedProperties2 + { + @JsonProperty("renamed") + public int getValue() { return 1; } + public void setValue(int x) { } + } + + // Testing that we can "merge" properties with renaming + static class MergedProperties { + public int x; + + @JsonProperty("x") + public void setFoobar(int v) { x = v; } + } + + // Testing that property order is obeyed, even for deserialization purposes + @JsonPropertyOrder({"a", "b", "c", "d"}) + static class SortedProperties + { + public int b; + public int c; + + public void setD(int value) { } + public void setA(int value) { } + } + + // [JACKSON-700]: test property type detection, selection + static class TypeTestBean + { + protected Long value; + + @JsonCreator + public TypeTestBean(@JsonProperty("value") String value) { } + + // If you remove this method, the test will pass + public Integer getValue() { return 0; } + } + + static class Jackson703 + { + private List location = new ArrayList(); + + { + location.add(new FoodOrgLocation()); + } + + public List getLocation() { return location; } + } + + static class FoodOrgLocation + { + protected Long id; + public String name; + protected Location location; + + public FoodOrgLocation() { + location = new Location(); + } + + public FoodOrgLocation(final Location foodOrg) { } + + public FoodOrgLocation(final Long id, final String name, final Location location) { } + + public Location getLocation() { return location; } + } + + static class Location { + public BigDecimal lattitude; + public BigDecimal longitude; + + public Location() { } + + public Location(final BigDecimal lattitude, final BigDecimal longitude) { } + } + + class Issue701Bean { // important: non-static! + private int i; + + // annotation does not matter -- just need one on the last argument + public Issue701Bean(@JsonProperty int i) { this.i = i; } + + public int getX() { return i; } + } + + static class Issue744Bean + { + protected Map additionalProperties; + + @JsonAnySetter + public void addAdditionalProperty(String key, Object value) { + if (additionalProperties == null) additionalProperties = new HashMap(); + additionalProperties.put(key,value); + } + + public void setAdditionalProperties(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { return additionalProperties; } + + @JsonIgnore + public String getName() { + return (String) additionalProperties.get("name"); + } + } + + static class PropDescBean + { + public final static String A_DESC = "That's A!"; + public final static int B_INDEX = 3; + + @JsonPropertyDescription(A_DESC) + public String a; + + protected int b; + + public String getA() { return a; } + + public void setA(String a) { this.a = a; } + + @JsonProperty(required=true, index=B_INDEX, defaultValue="13") + public int getB() { return b; } + } + + @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @JacksonAnnotation + @interface A {} + + @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @JacksonAnnotation + @interface B {} + + static class DuplicateGetterBean + { + @A + public boolean isBloop() { return true; } + + @B + public boolean getBloop() { return true; } + } + + static class DuplicateGetterCreatorBean + { + public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) {} + + public boolean isBloop() { return true; } + + public boolean getBloop() { return true; } + } + + static class DuplicateSetterBean { + public void setBloop(Boolean bloop) { + } + + @JsonSetter + public void setBloop(Object bloop) { + } + } /* /********************************************************** @@ -331,313 +246,333 @@ public void setBloop(Object bloop) { /********************************************************** */ - private final ObjectMapper MAPPER = objectMapper(); - - public void testSimple() { - POJOPropertiesCollector coll = collector(MAPPER, - Simple.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("value"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleFieldVisibility() { - // false -> deserialization - POJOPropertiesCollector coll = collector(MAPPER, - SimpleFieldDeser.class, false); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("values"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleGetterVisibility() { - POJOPropertiesCollector coll = collector(MAPPER, - SimpleGetterVisibility.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("a"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - // Unit test for verifying that a single @JsonIgnore can remove the - // whole property, unless explicit property marker exists - public void testEmpty() { - POJOPropertiesCollector coll = collector(MAPPER, - Empty.class, true); - Map props = coll.getPropertyMap(); - assertEquals(0, props.size()); - } - - // Unit test for verifying handling of 'partial' @JsonIgnore; that is, - // if there is at least one explicit annotation to indicate property, - // only parts that are ignored are, well, ignored - public void testPartialIgnore() { - POJOPropertiesCollector coll = collector(MAPPER, - IgnoredSetter.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("value"); - assertNotNull(prop); - assertFalse(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleRenamed() { - POJOPropertiesCollector coll = collector(MAPPER, - RenamedProperties.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("x"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleRenamed2() { - POJOPropertiesCollector coll = collector(MAPPER, - RenamedProperties2.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("renamed"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertTrue(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - public void testMergeWithRename() { - POJOPropertiesCollector coll = collector(MAPPER, - MergedProperties.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("x"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertTrue(prop.hasField()); - } - - public void testSimpleIgnoreAndRename() { - POJOPropertiesCollector coll = collector(MAPPER, - IgnoredRenamedSetter.class, true); - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = props.get("y"); - assertNotNull(prop); - assertTrue(prop.hasSetter()); - assertFalse(prop.hasGetter()); - assertFalse(prop.hasField()); - } - - public void testGlobalVisibilityForGetters() { - ObjectMapper m = jsonMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) - .build(); - POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true); - // should be 1, expect that we disabled getter auto-detection, so - Map props = coll.getPropertyMap(); - assertEquals(0, props.size()); - } - - public void testCollectionOfIgnored() { - POJOPropertiesCollector coll = collector(MAPPER, ImplicitIgnores.class, false); - // should be 1, due to ignorals - Map props = coll.getPropertyMap(); - assertEquals(1, props.size()); - // but also have 2 ignored properties - Collection ign = coll.getIgnoredPropertyNames(); - assertEquals(2, ign.size()); - assertTrue(ign.contains("a")); - assertTrue(ign.contains("b")); - } - - public void testSimpleOrderingForDeserialization() { - POJOPropertiesCollector coll = collector(MAPPER, SortedProperties.class, false); - List props = coll.getProperties(); - assertEquals(4, props.size()); - assertEquals("a", props.get(0).getName()); - assertEquals("b", props.get(1).getName()); - assertEquals("c", props.get(2).getName()); - assertEquals("d", props.get(3).getName()); - } - - public void testSimpleWithType() { - // first for serialization; should base choice on getter - POJOPropertiesCollector coll = collector(MAPPER, TypeTestBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - assertEquals("value", props.get(0).getName()); - AnnotatedMember m = props.get(0).getAccessor(); - assertTrue(m instanceof AnnotatedMethod); - assertEquals(Integer.class, m.getRawType()); - - // then for deserialization; prefer ctor param - coll = collector(MAPPER, TypeTestBean.class, false); - props = coll.getProperties(); - assertEquals(1, props.size()); - assertEquals("value", props.get(0).getName()); - m = props.get(0).getMutator(); - assertEquals(AnnotatedParameter.class, m.getClass()); - assertEquals(String.class, m.getRawType()); - } - - public void testInnerClassWithAnnotationsInCreator() throws Exception { - BeanDescription beanDesc; - // first with serialization - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); - assertNotNull(beanDesc); - // then with deserialization - beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); - assertNotNull(beanDesc); - } - - public void testUseAnnotationsFalse() throws Exception { - // note: need a separate mapper, need to reconfigure - ObjectMapper mapper = jsonMapperBuilder() - .configure(MapperFeature.USE_ANNOTATIONS, false) - .build(); - BeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class)); - assertNotNull(beanDesc); - - Jackson703 bean = new Jackson703(); - String json = mapper.writeValueAsString(bean); - assertNotNull(json); - } - - public void testJackson744() throws Exception { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect - (MAPPER.constructType(Issue744Bean.class)); - assertNotNull(beanDesc); - AnnotatedMember setter = beanDesc.findAnySetterAccessor(); - assertNotNull(setter); - assertEquals("addAdditionalProperty", setter.getName()); - assertTrue(setter instanceof AnnotatedMethod); - } - - // [databind#269]: Support new @JsonPropertyDescription - public void testPropertyDesc() throws Exception { - // start via deser - BeanDescription beanDesc = MAPPER.getDeserializationConfig() - .introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, true, false, "13"); - // and then via ser: - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, true, false, "13"); - } - - // [databind#438]: Support @JsonProperty.index - public void testPropertyIndex() throws Exception { - BeanDescription beanDesc = MAPPER.getDeserializationConfig() - .introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, false, true, "13"); - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); - _verifyProperty(beanDesc, false, true, "13"); - } - - public void testDuplicateGetters() throws Exception { - POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - BeanPropertyDefinition prop = props.get(0); - assertEquals("bloop", prop.getName()); - assertTrue(prop.getGetter().hasAnnotation(A.class)); - assertTrue(prop.getGetter().hasAnnotation(B.class)); - } - - public void testDuplicateGettersCreator() throws Exception { - POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterCreatorBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - POJOPropertyBuilder prop = (POJOPropertyBuilder)props.get(0); - assertEquals("bloop", prop.getName()); - // Can't call getGetter or the duplicate will be removed - assertTrue(prop._getters.value.hasAnnotation(A.class)); - assertNotNull(prop._getters.next); - assertTrue(prop._getters.next.value.hasAnnotation(A.class)); - } - - public void testDuplicateSetters() throws Exception { - POJOPropertiesCollector coll = collector(MAPPER, DuplicateSetterBean.class, true); - List props = coll.getProperties(); - assertEquals(1, props.size()); - BeanPropertyDefinition prop = props.get(0); - assertEquals("bloop", prop.getName()); - assertEquals(prop.getSetter().getRawParameterType(0), Object.class); - } - - public void testDuplicateSettersHaveCustomNamingStrategy() throws Exception { - ObjectMapper mapper = newJsonMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE); - POJOPropertiesCollector coll = collector(mapper, DuplicateSetterBean.class, false); - List props = coll.getProperties(); - assertEquals(1, props.size()); - BeanPropertyDefinition prop = props.get(0); - assertEquals("bloop", prop.getName()); - assertEquals(prop.getSetter().getRawParameterType(0), Object.class); - } - - private void _verifyProperty(BeanDescription beanDesc, - boolean verifyDesc, boolean verifyIndex, String expDefaultValue) { - assertNotNull(beanDesc); - List props = beanDesc.findProperties(); - assertEquals(2, props.size()); - for (BeanPropertyDefinition prop : props) { - String name = prop.getName(); - final PropertyMetadata md = prop.getMetadata(); - if ("a".equals(name)) { - assertFalse(md.isRequired()); - assertNull(md.getRequired()); - if (verifyDesc) { - assertEquals(PropDescBean.A_DESC, md.getDescription()); - } - if (verifyIndex) { - assertNull(md.getIndex()); - } - } else if ("b".equals(name)) { - assertTrue(md.isRequired()); - assertEquals(Boolean.TRUE, md.getRequired()); - if (verifyDesc) { - assertNull(md.getDescription()); - } - if (verifyIndex) { - assertEquals(Integer.valueOf(PropDescBean.B_INDEX), md.getIndex()); - } - if (expDefaultValue != null) { - assertEquals(expDefaultValue, md.getDefaultValue()); - } - } else { - fail("Unrecognized property '" + name + "'"); - } - } - } + private final ObjectMapper MAPPER = objectMapper(); + + public void testSimple() + { + POJOPropertiesCollector coll = collector(MAPPER, + Simple.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("value"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleFieldVisibility() + { + // false -> deserialization + POJOPropertiesCollector coll = collector(MAPPER, + SimpleFieldDeser.class, false); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("values"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleGetterVisibility() + { + POJOPropertiesCollector coll = collector(MAPPER, + SimpleGetterVisibility.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("a"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + // Unit test for verifying that a single @JsonIgnore can remove the + // whole property, unless explicit property marker exists + public void testEmpty() + { + POJOPropertiesCollector coll = collector(MAPPER, + Empty.class, true); + Map props = coll.getPropertyMap(); + assertEquals(0, props.size()); + } + + // Unit test for verifying handling of 'partial' @JsonIgnore; that is, + // if there is at least one explicit annotation to indicate property, + // only parts that are ignored are, well, ignored + public void testPartialIgnore() + { + POJOPropertiesCollector coll = collector(MAPPER, + IgnoredSetter.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("value"); + assertNotNull(prop); + assertFalse(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleRenamed() + { + POJOPropertiesCollector coll = collector(MAPPER, + RenamedProperties.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("x"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleRenamed2() + { + POJOPropertiesCollector coll = collector(MAPPER, + RenamedProperties2.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("renamed"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertTrue(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + public void testMergeWithRename() + { + POJOPropertiesCollector coll = collector(MAPPER, + MergedProperties.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("x"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertTrue(prop.hasField()); + } + + public void testSimpleIgnoreAndRename() + { + POJOPropertiesCollector coll = collector(MAPPER, + IgnoredRenamedSetter.class, true); + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = props.get("y"); + assertNotNull(prop); + assertTrue(prop.hasSetter()); + assertFalse(prop.hasGetter()); + assertFalse(prop.hasField()); + } + + public void testGlobalVisibilityForGetters() + { + ObjectMapper m = jsonMapperBuilder() + .configure(MapperFeature.AUTO_DETECT_GETTERS, false) + .build(); + POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true); + // should be 1, expect that we disabled getter auto-detection, so + Map props = coll.getPropertyMap(); + assertEquals(0, props.size()); + } + + public void testCollectionOfIgnored() + { + POJOPropertiesCollector coll = collector(MAPPER, ImplicitIgnores.class, false); + // should be 1, due to ignorals + Map props = coll.getPropertyMap(); + assertEquals(1, props.size()); + // but also have 2 ignored properties + Collection ign = coll.getIgnoredPropertyNames(); + assertEquals(2, ign.size()); + assertTrue(ign.contains("a")); + assertTrue(ign.contains("b")); + } + + public void testSimpleOrderingForDeserialization() + { + POJOPropertiesCollector coll = collector(MAPPER, SortedProperties.class, false); + List props = coll.getProperties(); + assertEquals(4, props.size()); + assertEquals("a", props.get(0).getName()); + assertEquals("b", props.get(1).getName()); + assertEquals("c", props.get(2).getName()); + assertEquals("d", props.get(3).getName()); + } + + public void testSimpleWithType() + { + // first for serialization; should base choice on getter + POJOPropertiesCollector coll = collector(MAPPER, TypeTestBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + assertEquals("value", props.get(0).getName()); + AnnotatedMember m = props.get(0).getAccessor(); + assertTrue(m instanceof AnnotatedMethod); + assertEquals(Integer.class, m.getRawType()); + + // then for deserialization; prefer ctor param + coll = collector(MAPPER, TypeTestBean.class, false); + props = coll.getProperties(); + assertEquals(1, props.size()); + assertEquals("value", props.get(0).getName()); + m = props.get(0).getMutator(); + assertEquals(AnnotatedParameter.class, m.getClass()); + assertEquals(String.class, m.getRawType()); + } + + public void testInnerClassWithAnnotationsInCreator() throws Exception + { + BeanDescription beanDesc; + // first with serialization + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + assertNotNull(beanDesc); + // then with deserialization + beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + assertNotNull(beanDesc); + } + + public void testUseAnnotationsFalse() throws Exception + { + // note: need a separate mapper, need to reconfigure + ObjectMapper mapper = jsonMapperBuilder() + .configure(MapperFeature.USE_ANNOTATIONS, false) + .build(); + BeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class)); + assertNotNull(beanDesc); + + Jackson703 bean = new Jackson703(); + String json = mapper.writeValueAsString(bean); + assertNotNull(json); + } + + public void testJackson744() throws Exception + { + BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect + (MAPPER.constructType(Issue744Bean.class)); + assertNotNull(beanDesc); + AnnotatedMember setter = beanDesc.findAnySetterAccessor(); + assertNotNull(setter); + assertEquals("addAdditionalProperty", setter.getName()); + assertTrue(setter instanceof AnnotatedMethod); + } + + // [databind#269]: Support new @JsonPropertyDescription + public void testPropertyDesc() throws Exception + { + // start via deser + BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, true, false, "13"); + // and then via ser: + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, true, false, "13"); + } + + // [databind#438]: Support @JsonProperty.index + public void testPropertyIndex() throws Exception + { + BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, false, true, "13"); + beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + _verifyProperty(beanDesc, false, true, "13"); + } + + public void testDuplicateGetters() throws Exception + { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertTrue(prop.getGetter().hasAnnotation(A.class)); + assertTrue(prop.getGetter().hasAnnotation(B.class)); + } + + public void testDuplicateGettersCreator() throws Exception + { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateGetterCreatorBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + POJOPropertyBuilder prop = (POJOPropertyBuilder) props.get(0); + assertEquals("bloop", prop.getName()); + // Can't call getGetter or the duplicate will be removed + assertTrue(prop._getters.value.hasAnnotation(A.class)); + assertNotNull(prop._getters.next); + assertTrue(prop._getters.next.value.hasAnnotation(A.class)); + } + + public void testDuplicateSetters() throws Exception { + POJOPropertiesCollector coll = collector(MAPPER, DuplicateSetterBean.class, true); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertEquals(prop.getSetter().getRawParameterType(0), Object.class); + } + + public void testDuplicateSettersHaveCustomNamingStrategy() throws Exception { + ObjectMapper mapper = newJsonMapper(); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE); + POJOPropertiesCollector coll = collector(mapper, DuplicateSetterBean.class, false); + List props = coll.getProperties(); + assertEquals(1, props.size()); + BeanPropertyDefinition prop = props.get(0); + assertEquals("bloop", prop.getName()); + assertEquals(prop.getSetter().getRawParameterType(0), Object.class); + } + + private void _verifyProperty(BeanDescription beanDesc, + boolean verifyDesc, boolean verifyIndex, String expDefaultValue) + { + assertNotNull(beanDesc); + List props = beanDesc.findProperties(); + assertEquals(2, props.size()); + for (BeanPropertyDefinition prop : props) { + String name = prop.getName(); + final PropertyMetadata md = prop.getMetadata(); + if ("a".equals(name)) { + assertFalse(md.isRequired()); + assertNull(md.getRequired()); + if (verifyDesc) { + assertEquals(PropDescBean.A_DESC, md.getDescription()); + } + if (verifyIndex) { + assertNull(md.getIndex()); + } + } else if ("b".equals(name)) { + assertTrue(md.isRequired()); + assertEquals(Boolean.TRUE, md.getRequired()); + if (verifyDesc) { + assertNull(md.getDescription()); + } + if (verifyIndex) { + assertEquals(Integer.valueOf(PropDescBean.B_INDEX), md.getIndex()); + } + if (expDefaultValue != null) { + assertEquals(expDefaultValue, md.getDefaultValue()); + } + } else { + fail("Unrecognized property '"+name+"'"); + } + } + } /* /********************************************************** /* Helper methods /********************************************************** */ - protected POJOPropertiesCollector collector(ObjectMapper m0, - Class cls, boolean forSerialization) { - BasicClassIntrospector bci = new BasicClassIntrospector(); - // no real difference between serialization, deserialization, at least here - if (forSerialization) { - return bci.collectProperties(m0.getSerializationConfig(), - m0.constructType(cls), null, true); - } - return bci.collectProperties(m0.getDeserializationConfig(), - m0.constructType(cls), null, false); - } + protected POJOPropertiesCollector collector(ObjectMapper m0, + Class cls, boolean forSerialization) + { + BasicClassIntrospector bci = new BasicClassIntrospector(); + // no real difference between serialization, deserialization, at least here + if (forSerialization) { + return bci.collectProperties(m0.getSerializationConfig(), + m0.constructType(cls), null, true); + } + return bci.collectProperties(m0.getDeserializationConfig(), + m0.constructType(cls), null, false); + } }