diff options
author | Tatu Saloranta <tatu.saloranta@iki.fi> | 2017-07-19 13:06:29 -0700 |
---|---|---|
committer | Tatu Saloranta <tatu.saloranta@iki.fi> | 2017-07-19 13:06:29 -0700 |
commit | 574d2bcaf6a8a3749217dcba27698ebcdc648572 (patch) | |
tree | 758ee1baa2fd2ff421fcb94bfe60e719ba93f2f3 | |
parent | 445bd4482ff7e7fb2b222f410a5c9208f2a31c57 (diff) | |
download | jackson-databind-574d2bcaf6a8a3749217dcba27698ebcdc648572.tar.gz |
Add failing test for #1649
7 files changed, 149 insertions, 109 deletions
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java index 0d5beb009..0c09ae156 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java @@ -6,6 +6,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -1016,6 +1017,96 @@ public abstract class BasicSerializerFactory /* /********************************************************** + /* Factory methods for Reference types + /* (demoted from BeanSF down here in 2.9) + /********************************************************** + */ + + /** + * @since 2.7 + */ + public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType, + BeanDescription beanDesc, boolean staticTyping) + throws JsonMappingException + { + JavaType contentType = refType.getContentType(); + TypeSerializer contentTypeSerializer = contentType.getTypeHandler(); + final SerializationConfig config = prov.getConfig(); + if (contentTypeSerializer == null) { + contentTypeSerializer = createTypeSerializer(config, contentType); + } + JsonSerializer<Object> contentSerializer = contentType.getValueHandler(); + for (Serializers serializers : customSerializers()) { + JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc, + contentTypeSerializer, contentSerializer); + if (ser != null) { + return ser; + } + } + if (refType.isTypeOrSubTypeOf(AtomicReference.class)) { + return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping, + contentTypeSerializer, contentSerializer); + } + return null; + } + + protected JsonSerializer<?> buildAtomicReferenceSerializer(SerializerProvider prov, + ReferenceType refType, BeanDescription beanDesc, boolean staticTyping, + TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentSerializer) + throws JsonMappingException + { + final JavaType contentType = refType.getReferencedType(); + JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, + contentType, AtomicReference.class); + + // Need to support global legacy setting, for now: + JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); + Object valueToSuppress; + boolean suppressNulls; + + if (incl == JsonInclude.Include.USE_DEFAULTS + || incl == JsonInclude.Include.ALWAYS) { + valueToSuppress = null; + suppressNulls = false; + } else { + suppressNulls = true; + switch (incl) { + case NON_DEFAULT: + valueToSuppress = BeanUtil.getDefaultValue(contentType); + if (valueToSuppress != null) { + if (valueToSuppress.getClass().isArray()) { + valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); + } + } + break; + case NON_ABSENT: + valueToSuppress = contentType.isReferenceType() + ? MapSerializer.MARKER_FOR_EMPTY : null; + break; + case NON_EMPTY: + valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; + break; + case CUSTOM: + valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); + if (valueToSuppress == null) { // is this legal? + suppressNulls = true; + } else { + suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); + } + break; + case NON_NULL: + default: // should not matter but... + valueToSuppress = null; + break; + } + } + AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping, + contentTypeSerializer, contentSerializer); + return ser.withContentInclusion(valueToSuppress, suppressNulls); + } + + /* + /********************************************************** /* Factory methods, for non-container types /********************************************************** */ diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java index af05bb4af..004025630 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java @@ -1,10 +1,8 @@ package com.fasterxml.jackson.databind.ser; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; @@ -18,12 +16,9 @@ import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter; import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; -import com.fasterxml.jackson.databind.ser.std.AtomicReferenceSerializer; import com.fasterxml.jackson.databind.ser.std.MapSerializer; import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer; import com.fasterxml.jackson.databind.type.ReferenceType; -import com.fasterxml.jackson.databind.util.ArrayBuilders; -import com.fasterxml.jackson.databind.util.BeanUtil; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.Converter; @@ -285,89 +280,6 @@ public class BeanSerializerFactory } /** - * @since 2.7 - */ - public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType, - BeanDescription beanDesc, boolean staticTyping) - throws JsonMappingException - { - JavaType contentType = refType.getContentType(); - TypeSerializer contentTypeSerializer = contentType.getTypeHandler(); - final SerializationConfig config = prov.getConfig(); - if (contentTypeSerializer == null) { - contentTypeSerializer = createTypeSerializer(config, contentType); - } - JsonSerializer<Object> contentSerializer = contentType.getValueHandler(); - for (Serializers serializers : customSerializers()) { - JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc, - contentTypeSerializer, contentSerializer); - if (ser != null) { - return ser; - } - } - if (refType.isTypeOrSubTypeOf(AtomicReference.class)) { - return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping, - contentTypeSerializer, contentSerializer); - } - return null; - } - - protected JsonSerializer<?> buildAtomicReferenceSerializer(SerializerProvider prov, - ReferenceType refType, BeanDescription beanDesc, boolean staticTyping, - TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentSerializer) - throws JsonMappingException - { - final JavaType contentType = refType.getReferencedType(); - JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, - contentType, AtomicReference.class); - - // Need to support global legacy setting, for now: - JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); - Object valueToSuppress; - boolean suppressNulls; - - if (incl == JsonInclude.Include.USE_DEFAULTS - || incl == JsonInclude.Include.ALWAYS) { - valueToSuppress = null; - suppressNulls = false; - } else { - suppressNulls = true; - switch (incl) { - case NON_DEFAULT: - valueToSuppress = BeanUtil.getDefaultValue(contentType); - if (valueToSuppress != null) { - if (valueToSuppress.getClass().isArray()) { - valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); - } - } - break; - case NON_ABSENT: - valueToSuppress = contentType.isReferenceType() - ? MapSerializer.MARKER_FOR_EMPTY : null; - break; - case NON_EMPTY: - valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; - break; - case CUSTOM: - valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); - if (valueToSuppress == null) { // is this legal? - suppressNulls = true; - } else { - suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); - } - break; - case NON_NULL: - default: // should not matter but... - valueToSuppress = null; - break; - } - } - AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping, - contentTypeSerializer, contentSerializer); - return ser.withContentInclusion(valueToSuppress, suppressNulls); - } - - /** * Method called to create a type information serializer for values of given * non-container property * if one is needed. If not needed (no polymorphic handling configured), should @@ -480,10 +392,9 @@ public class BeanSerializerFactory } } - /* And if Object Id is needed, some preparation for that as well: better - * do before view handling, mostly for the custom id case which needs - * access to a property - */ + // And if Object Id is needed, some preparation for that as well: better + // do before view handling, mostly for the custom id case which needs + // access to a property builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props)); builder.setProperties(props); @@ -562,9 +473,8 @@ public class BeanSerializerFactory BeanPropertyWriter prop = props.get(i); if (propName.equals(prop.getName())) { idProp = prop; - /* Let's force it to be the first property to output - * (although it may still get rearranged etc) - */ + // Let's force it to be the first property to output + // (although it may still get rearranged etc) if (i > 0) { props.remove(i); props.add(0, idProp); diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java index 080a13c9b..43602e351 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java @@ -107,10 +107,9 @@ public class PropertyBuilder // Container types can have separate type serializers for content (value / element) type if (contentTypeSer != null) { - /* 04-Feb-2010, tatu: Let's force static typing for collection, if there is - * type information for contents. Should work well (for JAXB case); can be - * revisited if this causes problems. - */ + // 04-Feb-2010, tatu: Let's force static typing for collection, if there is + // type information for contents. Should work well (for JAXB case); can be + // revisited if this causes problems. if (serializationType == null) { // serializationType = TypeFactory.type(am.getGenericType(), _beanDesc.getType()); serializationType = declaredType; @@ -149,6 +148,7 @@ public class PropertyBuilder // property annotation override inclV = inclV.withOverrides(propDef.findInclusion()); + JsonInclude.Include inclusion = inclV.getValueInclusion(); if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // should not occur but... inclusion = JsonInclude.Include.ALWAYS; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java index db75c5e1a..a95d81203 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java @@ -438,7 +438,7 @@ public abstract class BeanSerializerBase // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes } else if (shape == JsonFormat.Shape.NATURAL) { if (_beanType.isMapLikeType() && Map.class.isAssignableFrom(_handledType)) { -; + ; } else if (Map.Entry.class.isAssignableFrom(_handledType)) { JavaType mapEntryType = _beanType.findSuperType(Map.Entry.class); @@ -726,10 +726,9 @@ public abstract class BeanSerializerBase String name = (i == props.length) ? "[anySetter]" : props[i].getName(); wrapAndThrow(provider, e, bean, name); } catch (StackOverflowError e) { - /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not - * have many stack frames to spare... just one or two; can't - * make many calls. - */ + // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many + // stack frames to spare... just one or two; can't make many calls. + // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly: //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java index 424cb3a3a..b4d6bb9e6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java @@ -108,7 +108,7 @@ public class MapSerializer * Set of entries to omit during serialization, if any */ protected final Set<String> _ignoredEntries; - + /** * Id of the property filter to use, if any; null if none. * diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java index cb8eb094c..cdd0a944f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java @@ -1,8 +1,7 @@ package com.fasterxml.jackson.databind.ser.filter; import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.*; @@ -41,7 +40,7 @@ public class MapInclusionTest extends BaseMapTest return this; } } - + /* /********************************************************** /* Test methods @@ -67,7 +66,7 @@ public class MapInclusionTest extends BaseMapTest String json = MAPPER.writeValueAsString(input); assertEquals(aposToQuotes("{'stuff':{'b':''}}"), json); } - + public void testNonEmptyNoNullsMap() throws IOException { NoNullsNotEmptyMapContainer input = new NoNullsNotEmptyMapContainer() diff --git a/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java b/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java new file mode 100644 index 000000000..d2d0906d4 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java @@ -0,0 +1,41 @@ +package com.fasterxml.jackson.failing; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.*; + +public class MapInclusion1649Test extends BaseMapTest +{ + @JsonInclude(value=JsonInclude.Include.NON_EMPTY, content=JsonInclude.Include.NON_EMPTY) + static class Bean1649 { + public Map<String, String> map; + + public Bean1649(String key, String value) { + map = new LinkedHashMap<>(); + map.put(key, value); + } + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + final private ObjectMapper MAPPER = objectMapper(); + + // [databind#1649] + public void testNonEmptyViaClass() throws IOException + { + // non-empty/null, include + assertEquals(aposToQuotes("{'map':{'a':'b'}}"), + MAPPER.writeValueAsString(new Bean1649("a", "b"))); + // null, empty, nope + assertEquals(aposToQuotes("{}"), + MAPPER.writeValueAsString(new Bean1649("a", null))); + assertEquals(aposToQuotes("{}"), + MAPPER.writeValueAsString(new Bean1649("a", ""))); + } +} |