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,
/** * 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 null (which is not allowed)
Since:2.8
/** * 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
/** * 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 BeanDeserializerBuilder.
/** * 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.
/** * 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 withIgnoreAllUnknown(boolean ignoreUnknown) { return new BeanDeserializer(this, ignoreUnknown); } @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).
/** * 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 if (t != null) { 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, or UNWRAP_SINGLE_ARRAY 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(getValueType(ctxt), 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.
/** * 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.
/** * 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.
/** * 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); /* 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.
/** * 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 } // 13-Apr-2020, tatu: [databind#2678] need to handle injection here if (_injectables != null) { injectValues(ctxt, bean); } 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
/** * @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 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
/** * 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(getValueType(ctxt), p); } @Override protected Object _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException { // note: cannot call `_delegateDeserializer()` since order reversed here: JsonDeserializer<Object> delegateDeser = _arrayDelegateDeserializer; // fallback to non-array delegate if ((delegateDeser != null) || ((delegateDeser = _delegateDeserializer) != null)) { Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { JsonToken t = p.nextToken(); if (t == JsonToken.END_ARRAY && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { return null; } final Object value = deserialize(p, ctxt); if (p.nextToken() != JsonToken.END_ARRAY) { handleMissingEndArrayForSingle(p, ctxt); } return value; } if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { JsonToken t = p.nextToken(); if (t == JsonToken.END_ARRAY) { return null; } return ctxt.handleUnexpectedToken(getValueType(ctxt), JsonToken.START_ARRAY, p, null); } return ctxt.handleUnexpectedToken(getValueType(ctxt), 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
/** * 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) { // NOTE: do NOT skip name as it needs to be copied; `copyCurrentStructure` does that tokens.copyCurrentStructure(p); t = p.nextToken(); } // 28-Aug-2018, tatu: Let's add sanity check here, easier to catch off-by-some // problems if we maintain invariants if (t != JsonToken.END_OBJECT) { ctxt.reportWrongTokenException(this, JsonToken.END_OBJECT, "Attempted to unwrap '%s' value", handledType().getName()); } 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, "Cannot 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( "Cannot 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)); continue; } // Unknown: let's call handler method handleUnknownProperty(p, ctxt, _valueClass, propName); } tokens.writeEndObject(); // 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 DeserializationContext.handleInstantiationProblem(Class<?>, Object, Throwable).
Since:2.8
/** * 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
/** * @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, "Cannot resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved", _prop.getName(), _prop.getDeclaringClass().getName()); } _prop.set(_bean, value); } } }