package com.fasterxml.jackson.databind.deser; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; import com.fasterxml.jackson.databind.util.NameTransformer; import com.fasterxml.jackson.databind.util.TokenBuffer; /** * Deserializer class that can deserialize instances of * arbitrary bean objects, usually from JSON Object structs, */ public class BeanDeserializer extends BeanDeserializerBase implements java.io.Serializable { /* TODOs for future versions: * * For 2.9? * * - New method in JsonDeserializer (deserializeNext()) to allow use of more * efficient 'nextXxx()' method `JsonParser` provides. * * Also: need to ensure efficient impl of those methods for Smile, CBOR * at least (in addition to JSON) */ private static final long serialVersionUID = 1L; /** * Lazily constructed exception used as root cause if reporting problem * with creator method that returns <code>null</code> (which is not allowed) * * @since 2.8 */ 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 /********************************************************** */ /** * Constructor used by {@link BeanDeserializerBuilder}. */ public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs, HashSet<String> ignorableProps, boolean ignoreAllUnknown, boolean hasViews) { super(builder, beanDesc, properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews); } /** * Copy-constructor that can be used by sub-classes to allow * copy-on-write style copying of settings of an existing instance. */ protected BeanDeserializer(BeanDeserializerBase src) { super(src, src._ignoreAllUnknown); } protected BeanDeserializer(BeanDeserializerBase src, boolean ignoreAllUnknown) { super(src, ignoreAllUnknown); } protected BeanDeserializer(BeanDeserializerBase src, NameTransformer unwrapper) { super(src, unwrapper); } public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) { super(src, oir); } public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps) { super(src, ignorableProps); } public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) { super(src, props); } @Override public JsonDeserializer<Object> 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; } // 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 public BeanDeserializer withObjectIdReader(ObjectIdReader oir) { return new BeanDeserializer(this, oir); } @Override public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps) { return new BeanDeserializer(this, ignorableProps); } @Override public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { return new BeanDeserializer(this, props); } @Override protected BeanDeserializerBase asArrayDeserializer() { SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); return new BeanAsArrayDeserializer(this, props); } /* /********************************************************** /* JsonDeserializer implementation /********************************************************** */ /** * Main deserialization method for bean-based objects (POJOs). */ @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // common case first if (p.isExpectedStartObjectToken()) { if (_vanillaProcessing) { return vanillaDeserialize(p, ctxt, p.nextToken()); } // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is // what it is, including "expected behavior". p.nextToken(); if (_objectIdReader != null) { return deserializeWithObjectId(p, ctxt); } return deserializeFromObject(p, ctxt); } return _deserializeOther(p, ctxt, p.getCurrentToken()); } protected final Object _deserializeOther(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException { // and then others, generally requiring use of @JsonCreator switch (t) { case VALUE_STRING: return deserializeFromString(p, ctxt); case VALUE_NUMBER_INT: return deserializeFromNumber(p, ctxt); case VALUE_NUMBER_FLOAT: return deserializeFromDouble(p, ctxt); case VALUE_EMBEDDED_OBJECT: return deserializeFromEmbedded(p, ctxt); case VALUE_TRUE: case VALUE_FALSE: return deserializeFromBoolean(p, ctxt); case VALUE_NULL: return deserializeFromNull(p, ctxt); case START_ARRAY: // these only work if there's a (delegating) creator... return deserializeFromArray(p, ctxt); case FIELD_NAME: case END_OBJECT: // added to resolve [JACKSON-319], possible related issues if (_vanillaProcessing) { return vanillaDeserialize(p, ctxt, t); } if (_objectIdReader != null) { return deserializeWithObjectId(p, ctxt); } return deserializeFromObject(p, ctxt); default: } return ctxt.handleUnexpectedToken(handledType(), p); } @Deprecated // since 2.8; remove unless getting used protected Object _missingToken(JsonParser p, DeserializationContext ctxt) throws IOException { throw ctxt.endOfInputException(handledType()); } /** * Secondary deserialization method, called in cases where POJO * instance is created as part of deserialization, potentially * after collecting some or all of the properties to set. */ @Override public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException { // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); if (_injectables != null) { injectValues(ctxt, bean); } if (_unwrappedPropertyHandler != null) { return deserializeWithUnwrapped(p, ctxt, bean); } if (_externalTypeIdHandler != null) { return deserializeWithExternalTypeId(p, ctxt, bean); } String propName; // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... if (p.isExpectedStartObjectToken()) { propName = p.nextFieldName(); if (propName == null) { return bean; } } else { if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { propName = p.getCurrentName(); } else { return bean; } } if (_needViewProcesing) { Class<?> view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } do { p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } while ((propName = p.nextFieldName()) != null); return bean; } /* /********************************************************** /* Concrete deserialization methods /********************************************************** */ /** * Streamlined version that is only used when no "special" * features are enabled. */ private final Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException { final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { String propName = p.getCurrentName(); do { p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } while ((propName = p.nextFieldName()) != null); } return bean; } /** * General version used when handling needs more advanced features. */ @Override public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException { /* 09-Dec-2014, tatu: As per [databind#622], we need to allow Object Id references * to come in as JSON Objects as well; but for now assume they will * be simple, single-property references, which means that we can * recognize them without having to buffer anything. * Once again, if we must, we can do more complex handling with buffering, * but let's only do that if and when that becomes necessary. */ if (_objectIdReader != null && _objectIdReader.maySerializeAsObject()) { if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME) && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { return deserializeFromObjectId(p, ctxt); } } if (_nonStandardCreation) { if (_unwrappedPropertyHandler != null) { return deserializeWithUnwrapped(p, ctxt); } if (_externalTypeIdHandler != null) { return deserializeWithExternalTypeId(p, ctxt); } Object bean = deserializeFromObjectUsingNonDefault(p, ctxt); if (_injectables != null) { injectValues(ctxt, bean); } /* 27-May-2014, tatu: I don't think view processing would work * at this point, so commenting it out; but leaving in place * just in case I forgot something fundamental... */ /* if (_needViewProcesing) { Class<?> view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } */ return bean; } final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); if (p.canReadObjectId()) { Object id = p.getObjectId(); if (id != null) { _handleTypedObjectId(p, ctxt, bean, id); } } if (_injectables != null) { injectValues(ctxt, bean); } if (_needViewProcesing) { Class<?> view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { String propName = p.getCurrentName(); do { p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } while ((propName = p.nextFieldName()) != null); } return bean; } /** * Method called to deserialize bean using "property-based creator": * this means that a non-default constructor or factory method is * called, and then possibly other setters. The trick is that * values for creator method need to be buffered, first; and * due to non-guaranteed ordering possibly some other properties * as well. */ @Override @SuppressWarnings("resource") protected Object _deserializeUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt) throws IOException { final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); TokenBuffer unknown = null; final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null; JsonToken t = p.getCurrentToken(); List<BeanReferring> referrings = null; for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); p.nextToken(); // to point to value // Object Id property? if (buffer.readIdProperty(propName)) { continue; } // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); if (creatorProp != null) { // Last creator property to set? Object value; if ((activeView != null) && !creatorProp.visibleInView(activeView)) { p.skipChildren(); continue; } value = _deserializeWithErrorWrapping(p, ctxt, creatorProp); if (buffer.assignParameter(creatorProp, value)) { p.nextToken(); // to move to following FIELD_NAME/END_OBJECT Object bean; try { bean = creator.build(ctxt, buffer); } catch (Exception e) { bean = wrapInstantiationProblem(e, ctxt); } if (bean == null) { return ctxt.handleInstantiationProblem(handledType(), null, _creatorReturnedNullException()); } // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); // polymorphic? if (bean.getClass() != _beanType.getRawClass()) { return handlePolymorphic(p, ctxt, bean, unknown); } if (unknown != null) { // nope, just extra unknown stuff... bean = handleUnknownProperties(ctxt, bean, unknown); } // or just clean? return deserialize(p, ctxt, bean); } continue; } // regular property? needs buffering SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { try { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); } catch (UnresolvedForwardReference reference) { // 14-Jun-2016, tatu: As per [databind#1261], looks like we need additional // handling of forward references here. Not exactly sure why existing // facilities did not cover, but this does appear to solve the problem BeanReferring referring = handleUnresolvedReference(ctxt, prop, buffer, reference); if (referrings == null) { referrings = new ArrayList<BeanReferring>(); } referrings.add(referring); } continue; } // Things marked as ignorable should not be passed to any setter if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, handledType(), propName); continue; } // "any property"? if (_anySetter != null) { try { buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } continue; } // Ok then, let's collect the whole field; name and value if (unknown == null) { unknown = new TokenBuffer(p, ctxt); } unknown.writeFieldName(propName); unknown.copyCurrentStructure(p); } // We hit END_OBJECT, so: Object bean; try { bean = creator.build(ctxt, buffer); } catch (Exception e) { wrapInstantiationProblem(e, ctxt); bean = null; // never gets here } if (referrings != null) { for (BeanReferring referring : referrings) { referring.setBean(bean); } } if (unknown != null) { // polymorphic? if (bean.getClass() != _beanType.getRawClass()) { return handlePolymorphic(null, ctxt, bean, unknown); } // no, just some extra unknown properties return handleUnknownProperties(ctxt, bean, unknown); } return bean; } /** * @since 2.8 */ private BeanReferring handleUnresolvedReference(DeserializationContext ctxt, SettableBeanProperty prop, PropertyValueBuffer buffer, UnresolvedForwardReference reference) throws JsonMappingException { BeanReferring referring = new BeanReferring(ctxt, reference, prop.getType(), buffer, prop); reference.getRoid().appendReferring(referring); return referring; } protected final Object _deserializeWithErrorWrapping(JsonParser p, DeserializationContext ctxt, SettableBeanProperty prop) throws IOException { try { return prop.deserialize(p, ctxt); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), prop.getName(), ctxt); // never gets here, unless caller declines to throw an exception return null; } } /** * Helper method called for rare case of pointing to {@link JsonToken#VALUE_NULL} * token. While this is most often an erroneous condition, there is one specific * case with XML handling where polymorphic type with no properties is exposed * as such, and should be handled same as empty Object. * * @since 2.7 */ protected Object deserializeFromNull(JsonParser p, DeserializationContext ctxt) throws IOException { // 17-Dec-2015, tatu: Highly specialized case, mainly to support polymorphic // "empty" POJOs deserialized from XML, where empty XML tag synthesizes a // `VALUE_NULL` token. if (p.requiresCustomCodec()) { // not only XML module, but mostly it... @SuppressWarnings("resource") TokenBuffer tb = new TokenBuffer(p, ctxt); tb.writeEndObject(); JsonParser p2 = tb.asParser(p); p2.nextToken(); // to point to END_OBJECT // note: don't have ObjectId to consider at this point, so: Object ob = _vanillaProcessing ? vanillaDeserialize(p2, ctxt, JsonToken.END_OBJECT) : deserializeFromObject(p2, ctxt); p2.close(); return ob; } return ctxt.handleUnexpectedToken(handledType(), p); } /* /********************************************************** /* Deserializing when we have to consider an active View /********************************************************** */ protected final Object deserializeWithView(JsonParser p, DeserializationContext ctxt, Object bean, Class<?> activeView) throws IOException { if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { String propName = p.getCurrentName(); do { p.nextToken(); // TODO: 06-Jan-2015, tatu: try streamlining call sequences here as well SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { if (!prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } while ((propName = p.nextFieldName()) != null); } return bean; } /* /********************************************************** /* Handling for cases where we have "unwrapped" values /********************************************************** */ /** * Method called when there are declared "unwrapped" properties * which need special handling */ @SuppressWarnings("resource") protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt) throws IOException { if (_delegateDeserializer != null) { return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); } if (_propertyBasedCreator != null) { return deserializeUsingPropertyBasedWithUnwrapped(p, ctxt); } TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); if (_injectables != null) { injectValues(ctxt, bean); } final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null; String propName = p.hasTokenId(JsonTokenId.ID_FIELD_NAME) ? p.getCurrentName() : null; for (; propName != null; propName = p.nextFieldName()) { p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case if ((activeView != null) && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } // Things marked as ignorable should not be passed to any setter if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } // 29-Nov-2016, tatu: probably should try to avoid sending content // both to any setter AND buffer... but, for now, the only thing // we can do. // how about any setter? We'll get copies but... if (_anySetter == null) { // but... others should be passed to unwrapped property deserializers tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); continue; } // Need to copy to a separate buffer first TokenBuffer b2 = TokenBuffer.asCopyOfValue(p); tokens.writeFieldName(propName); tokens.append(b2); try { _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } } tokens.writeEndObject(); _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); return bean; } @SuppressWarnings("resource") protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException { JsonToken t = p.getCurrentToken(); if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null; for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); SettableBeanProperty prop = _beanProperties.find(propName); p.nextToken(); if (prop != null) { // normal case if (activeView != null && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } // 29-Nov-2016, tatu: probably should try to avoid sending content // both to any setter AND buffer... but, for now, the only thing // we can do. // how about any setter? We'll get copies but... if (_anySetter == null) { // but... others should be passed to unwrapped property deserializers tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); } else { // Need to copy to a separate buffer first TokenBuffer b2 = TokenBuffer.asCopyOfValue(p); tokens.writeFieldName(propName); tokens.append(b2); try { _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } } tokens.writeEndObject(); _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); return bean; } @SuppressWarnings("resource") protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt) throws IOException { // 01-Dec-2016, tatu: Note: This IS legal to call, but only when unwrapped // value itself is NOT passed via `CreatorProperty` (which isn't supported). // Ok however to pass via setter or field. final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); JsonToken t = p.getCurrentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); if (creatorProp != null) { // Last creator property to set? if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) { t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT Object bean; try { bean = creator.build(ctxt, buffer); } catch (Exception e) { bean = wrapInstantiationProblem(e, ctxt); } // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); // if so, need to copy all remaining tokens into buffer while (t == JsonToken.FIELD_NAME) { p.nextToken(); // to skip name tokens.copyCurrentStructure(p); t = p.nextToken(); } tokens.writeEndObject(); if (bean.getClass() != _beanType.getRawClass()) { // !!! 08-Jul-2011, tatu: Could probably support; but for now // it's too complicated, so bail out ctxt.reportInputMismatch(creatorProp, "Can not create polymorphic instances with unwrapped values"); return null; } return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); } continue; } // Object Id property? if (buffer.readIdProperty(propName)) { continue; } // regular property? needs buffering SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); continue; } // Things marked as ignorable should not be passed to any setter if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, handledType(), propName); continue; } // 29-Nov-2016, tatu: probably should try to avoid sending content // both to any setter AND buffer... but, for now, the only thing // we can do. // how about any setter? We'll get copies but... if (_anySetter == null) { // but... others should be passed to unwrapped property deserializers tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); } else { // Need to copy to a separate buffer first TokenBuffer b2 = TokenBuffer.asCopyOfValue(p); tokens.writeFieldName(propName); tokens.append(b2); try { buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(b2.asParserOnFirstToken(), ctxt)); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } continue; } } // We hit END_OBJECT, so: Object bean; try { bean = creator.build(ctxt, buffer); } catch (Exception e) { wrapInstantiationProblem(e, ctxt); return null; // never gets here } return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); } /* /********************************************************** /* Handling for cases where we have property/-ies with /* external type id /********************************************************** */ protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt) throws IOException { if (_propertyBasedCreator != null) { return deserializeUsingPropertyBasedWithExternalTypeId(p, ctxt); } if (_delegateDeserializer != null) { /* 24-Nov-2015, tatu: Use of delegating creator needs to have precedence, and basically * external type id handling just has to be ignored, as they would relate to target * type and not delegate type. Whether this works as expected is another story, but * there's no other way to really mix these conflicting features. */ return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); } return deserializeWithExternalTypeId(p, ctxt, _valueInstantiator.createUsingDefault(ctxt)); } protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException { final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null; final ExternalTypeHandler ext = _externalTypeIdHandler.start(); for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); t = p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case // [JACKSON-831]: may have property AND be used as external type id: if (t.isScalarValue()) { ext.handleTypePropertyValue(p, ctxt, propName, bean); } if (activeView != null && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } // ignorable things should be ignored if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } // but others are likely to be part of external type id thingy... if (ext.handlePropertyValue(p, ctxt, propName, bean)) { continue; } // if not, the usual fallback handling: if (_anySetter != null) { try { _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } // Unknown: let's call handler method handleUnknownProperty(p, ctxt, bean, propName); } // and when we get this far, let's try finalizing the deal: return ext.complete(p, ctxt, bean); } @SuppressWarnings("resource") protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, DeserializationContext ctxt) throws IOException { final ExternalTypeHandler ext = _externalTypeIdHandler.start(); final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); JsonToken t = p.getCurrentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); if (creatorProp != null) { // first: let's check to see if this might be part of value with external type id: // 11-Sep-2015, tatu: Important; do NOT pass buffer as last arg, but null, // since it is not the bean if (ext.handlePropertyValue(p, ctxt, propName, null)) { ; } else { // Last creator property to set? if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) { t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT Object bean; try { bean = creator.build(ctxt, buffer); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); continue; // never gets here } // if so, need to copy all remaining tokens into buffer while (t == JsonToken.FIELD_NAME) { p.nextToken(); // to skip name tokens.copyCurrentStructure(p); t = p.nextToken(); } if (bean.getClass() != _beanType.getRawClass()) { // !!! 08-Jul-2011, tatu: Could theoretically support; but for now // it's too complicated, so bail out return ctxt.reportBadDefinition(_beanType, String.format( "Can not create polymorphic instances with external type ids (%s -> %s)", _beanType, bean.getClass())); } return ext.complete(p, ctxt, bean); } } continue; } // Object Id property? if (buffer.readIdProperty(propName)) { continue; } // regular property? needs buffering SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); continue; } // external type id (or property that depends on it)? if (ext.handlePropertyValue(p, ctxt, propName, null)) { continue; } // Things marked as ignorable should not be passed to any setter if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, handledType(), propName); continue; } // "any property"? if (_anySetter != null) { buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); } } // We hit END_OBJECT; resolve the pieces: try { return ext.complete(p, ctxt, buffer, creator); } catch (Exception e) { return wrapInstantiationProblem(e, ctxt); } } /** * Helper method for getting a lazily construct exception to be reported * to {@link DeserializationContext#handleInstantiationProblem(Class, Object, Throwable)}. * * @since 2.8 */ protected Exception _creatorReturnedNullException() { if (_nullFromCreator == null) { _nullFromCreator = new NullPointerException("JSON Creator returned null"); } return _nullFromCreator; } /** * @since 2.8 */ static class BeanReferring extends Referring { private final DeserializationContext _context; private final SettableBeanProperty _prop; private Object _bean; BeanReferring(DeserializationContext ctxt, UnresolvedForwardReference ref, JavaType valueType, PropertyValueBuffer buffer, SettableBeanProperty prop) { super(ref, valueType); _context = ctxt; _prop = prop; } public void setBean(Object bean) { _bean = bean; } @Override public void handleResolvedForwardReference(Object id, Object value) throws IOException { if (_bean == null) { _context.reportInputMismatch(_prop, "Can not resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved", _prop.getName(), _prop.getDeclaringClass().getName()); } _prop.set(_bean, value); } } }