diff --git a/release-notes/VERSION b/release-notes/VERSION index f32efec009..05974262b7 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -13,6 +13,8 @@ Project: jackson-databind (reported by Starkom@github) #357: StackOverflowError with contentConverter that returns array type (reported by Florian S) +#383: Recursive `@JsonUnwrapped` (`child` with same type) fail: "No _valueDeserializer assigned" + (reported by tdavis@github) #403: Make FAIL_ON_NULL_FOR_PRIMITIVES apply to primitive arrays and other types that wrap primitives (reported by Harleen S) #476: Allow "Serialize as POJO" using `@JsonFormat(shape=Shape.OBJECT)` class annotation diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java index 3e7502d46b..89f51b54a5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java @@ -528,6 +528,8 @@ public JsonSerializer findValueSerializer(Class valueType, BeanProper * full generics-aware type instead of raw class. * This is necessary for accurate handling of external type information, * to handle polymorphic types. + *

+ * Note: this call will also contextualize serializer before returning it. * * @param property When creating secondary serializers, property for which * serializer is needed: annotations of the property (or bean that contains it) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 245d7baf14..7c5e7361c0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -39,6 +39,14 @@ public class BeanDeserializer */ protected transient Exception _nullFromCreator; + /** + * State marker we need in order to avoid infinite recursion for some cases + * (not very clean, alas, but has to do for now) + * + * @since 2.9 + */ + private volatile transient NameTransformer _currentlyTransforming; + /* /********************************************************** /* Life-cycle, construction, initialization @@ -86,18 +94,22 @@ public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) { } @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + public JsonDeserializer unwrappingDeserializer(NameTransformer transformer) { // bit kludgy but we don't want to accidentally change type; sub-classes // MUST override this method to support unwrapped properties... if (getClass() != BeanDeserializer.class) { return this; } - /* main thing really is to just enforce ignoring of unknown - * properties; since there may be multiple unwrapped values - * and properties for all may be interleaved... - */ - return new BeanDeserializer(this, unwrapper); + // 25-Mar-2017, tatu: Not clean at all, but for [databind#383] we do need + // to keep track of accidental recursion... + if (_currentlyTransforming == transformer) { + return this; + } + _currentlyTransforming = transformer; + try { + return new BeanDeserializer(this, transformer); + } finally { _currentlyTransforming = null; } } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java index 33c48a24f7..3c4aeb8fea 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java @@ -478,18 +478,23 @@ public void resolve(DeserializationContext ctxt) prop = _resolvedObjectIdProperty(ctxt, prop); } // Support unwrapped values (via @JsonUnwrapped) - SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop); - if (u != null) { - prop = u; - if (unwrapped == null) { - unwrapped = new UnwrappedPropertyHandler(); + NameTransformer xform = _findPropertyUnwrapper(ctxt, prop); + if (xform != null) { + JsonDeserializer orig = prop.getValueDeserializer(); + JsonDeserializer unwrapping = orig.unwrappingDeserializer(xform); + if (unwrapping != orig && unwrapping != null) { + prop = prop.withValueDeserializer(unwrapping); + if (unwrapped == null) { + unwrapped = new UnwrappedPropertyHandler(); + } + unwrapped.addProperty(prop); + // 12-Dec-2014, tatu: As per [databind#647], we will have problems if + // the original property is left in place. So let's remove it now. + // 25-Mar-2017, tatu: Wonder if this could be problematic wrt creators? + // (that is, should be remove it from creator too) + _beanProperties.remove(prop); + continue; } - unwrapped.addProperty(prop); - // 12-Dec-2014, tatu: As per [databind#647], we will have problems if - // the original property is left in place. So let's remove it now. - // 25-Mar-2017, tatu: Wonder if this could be problematic wrt creators? - _beanProperties.remove(prop); - continue; } // 26-Oct-2016, tatu: Need to have access to value deserializer to know if @@ -796,7 +801,7 @@ protected SettableBeanProperty _resolvedObjectIdProperty(DeserializationContext * Helper method called to see if given property might be so-called unwrapped * property: these require special handling. */ - protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext ctxt, + protected NameTransformer _findPropertyUnwrapper(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { @@ -811,13 +816,7 @@ protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext "Can not define Creator property \"%s\" as `@JsonUnwrapped`: combination not yet supported", prop.getName())); } - - JsonDeserializer orig = prop.getValueDeserializer(); - JsonDeserializer unwrapping = orig.unwrappingDeserializer(unwrapper); - if (unwrapping != orig && unwrapping != null) { - // might be cleaner to create new instance; but difficult to do reliably, so: - return prop.withValueDeserializer(unwrapping); - } + return unwrapper; } } return null; diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedRecursive383.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java similarity index 95% rename from src/test/java/com/fasterxml/jackson/failing/TestUnwrappedRecursive383.java rename to src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java index 76dda66ece..5b41be647e 100644 --- a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedRecursive383.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.failing; +package com.fasterxml.jackson.databind.struct; import com.fasterxml.jackson.annotation.*;