All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.databind.deser.BeanDeserializerBase Maven / Gradle / Ivy

There is a newer version: 2.17.0
Show newest version
package com.fasterxml.jackson.databind.deser;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.NumberType;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.exc.IgnoredPropertyException;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.ClassKey;
import com.fasterxml.jackson.databind.util.*;

/**
 * Base class for BeanDeserializer.
 */
public abstract class BeanDeserializerBase
    extends StdDeserializer
    implements ContextualDeserializer, ResolvableDeserializer,
        ValueInstantiator.Gettable, // since 2.9
        java.io.Serializable // since 2.1
{
    private static final long serialVersionUID = 1;

    protected final static PropertyName TEMP_PROPERTY_NAME = new PropertyName("#temporary-name");

    /*
    /**********************************************************
    /* Information regarding type being deserialized
    /**********************************************************
     */

    /**
     * Declared type of the bean this deserializer handles.
     */
    final protected JavaType _beanType;

    /**
     * Requested shape from bean class annotations.
     */
    final protected JsonFormat.Shape _serializationShape;

    /*
    /**********************************************************
    /* Configuration for creating value instance
    /**********************************************************
     */

    /**
     * Object that handles details of constructing initial 
     * bean value (to which bind data to), unless instance
     * is passed (via updateValue())
     */
    protected final ValueInstantiator _valueInstantiator;

    /**
     * Deserializer that is used iff delegate-based creator is
     * to be used for deserializing from JSON Object.
     */
    protected JsonDeserializer _delegateDeserializer;

    /**
     * Deserializer that is used iff array-delegate-based creator
     * is to be used for deserializing from JSON Object.
     */
    protected JsonDeserializer _arrayDelegateDeserializer;

    /**
     * If the bean needs to be instantiated using constructor
     * or factory method
     * that takes one or more named properties as argument(s),
     * this creator is used for instantiation.
     * This value gets resolved during general resolution.
     */
    protected PropertyBasedCreator _propertyBasedCreator;

    /**
     * Flag that is set to mark "non-standard" cases; where either
     * we use one of non-default creators, or there are unwrapped
     * values to consider.
     */
    protected boolean _nonStandardCreation;

    /**
     * Flag that indicates that no "special features" whatsoever
     * are enabled, so the simplest processing is possible.
     */
    protected boolean _vanillaProcessing;

    /*
    /**********************************************************
    /* Property information, setters
    /**********************************************************
     */

    /**
     * Mapping of property names to properties, built when all properties
     * to use have been successfully resolved.
     */
    final protected BeanPropertyMap _beanProperties;

    /**
     * List of {@link ValueInjector}s, if any injectable values are
     * expected by the bean; otherwise null.
     * This includes injectors used for injecting values via setters
     * and fields, but not ones passed through constructor parameters.
     */
    final protected ValueInjector[] _injectables;

    /**
     * Fallback setter used for handling any properties that are not
     * mapped to regular setters. If setter is not null, it will be
     * called once for each such property.
     */
    protected SettableAnyProperty _anySetter;

    /**
     * In addition to properties that are set, we will also keep
     * track of recognized but ignorable properties: these will
     * be skipped without errors or warnings.
     */
    final protected Set _ignorableProps;

    /**
     * Flag that can be set to ignore and skip unknown properties.
     * If set, will not throw an exception for unknown properties.
     */
    final protected boolean _ignoreAllUnknown;

    /**
     * Flag that indicates that some aspect of deserialization depends
     * on active view used (if any)
     */
    final protected boolean _needViewProcesing;

    /**
     * We may also have one or more back reference fields (usually
     * zero or one).
     */
    final protected Map _backRefs;

    /*
    /**********************************************************
    /* Related handlers
    /**********************************************************
     */

    /**
     * Lazily constructed map used to contain deserializers needed
     * for polymorphic subtypes.
     * Note that this is only needed for polymorphic types,
     * that is, when the actual type is not statically known.
     * For other types this remains null.
     */
    protected transient HashMap> _subDeserializers;

    /**
     * If one of properties has "unwrapped" value, we need separate
     * helper object
     */
    protected UnwrappedPropertyHandler _unwrappedPropertyHandler;

    /**
     * Handler that we need iff any of properties uses external
     * type id.
     */
    protected ExternalTypeHandler _externalTypeIdHandler;

    /**
     * If an Object Id is to be used for value handled by this
     * deserializer, this reader is used for handling.
     */
    protected final ObjectIdReader _objectIdReader;

    /*
    /**********************************************************
    /* Life-cycle, construction, initialization
    /**********************************************************
     */

    /**
     * Constructor used when initially building a deserializer
     * instance, given a {@link BeanDeserializerBuilder} that
     * contains configuration.
     */
    protected BeanDeserializerBase(BeanDeserializerBuilder builder,
            BeanDescription beanDesc,
            BeanPropertyMap properties, Map backRefs,
            Set ignorableProps, boolean ignoreAllUnknown,
            boolean hasViews)
    {
        super(beanDesc.getType());
        _beanType = beanDesc.getType();
        _valueInstantiator = builder.getValueInstantiator();
        
        _beanProperties = properties;
        _backRefs = backRefs;
        _ignorableProps = ignorableProps;
        _ignoreAllUnknown = ignoreAllUnknown;

        _anySetter = builder.getAnySetter();
        List injectables = builder.getInjectables();
        _injectables = (injectables == null || injectables.isEmpty()) ? null
                : injectables.toArray(new ValueInjector[injectables.size()]);
        _objectIdReader = builder.getObjectIdReader();
        _nonStandardCreation = (_unwrappedPropertyHandler != null)
            || _valueInstantiator.canCreateUsingDelegate()
            || _valueInstantiator.canCreateUsingArrayDelegate() // new in 2.7
            || _valueInstantiator.canCreateFromObjectWith()
            || !_valueInstantiator.canCreateUsingDefault()
            ;

        // Any transformation we may need to apply?
        JsonFormat.Value format = beanDesc.findExpectedFormat(null);
        _serializationShape = (format == null) ? null : format.getShape();

        _needViewProcesing = hasViews;
        _vanillaProcessing = !_nonStandardCreation
                && (_injectables == null)
                && !_needViewProcesing
                // also, may need to reorder stuff if we expect Object Id:
                && (_objectIdReader == null)
                ;
    }

    protected BeanDeserializerBase(BeanDeserializerBase src) {
        this(src, src._ignoreAllUnknown);
    }

    protected BeanDeserializerBase(BeanDeserializerBase src, boolean ignoreAllUnknown)
    {
        super(src._beanType);
        
        _beanType = src._beanType;
        
        _valueInstantiator = src._valueInstantiator;
        _delegateDeserializer = src._delegateDeserializer;
        _propertyBasedCreator = src._propertyBasedCreator;
        
        _beanProperties = src._beanProperties;
        _backRefs = src._backRefs;
        _ignorableProps = src._ignorableProps;
        _ignoreAllUnknown = ignoreAllUnknown;
        _anySetter = src._anySetter;
        _injectables = src._injectables;
        _objectIdReader = src._objectIdReader;
        
        _nonStandardCreation = src._nonStandardCreation;
        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
        _needViewProcesing = src._needViewProcesing;
        _serializationShape = src._serializationShape;

        _vanillaProcessing = src._vanillaProcessing;
    }

    protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapper)
    {
        super(src._beanType);

        _beanType = src._beanType;
        
        _valueInstantiator = src._valueInstantiator;
        _delegateDeserializer = src._delegateDeserializer;
        _propertyBasedCreator = src._propertyBasedCreator;

        _backRefs = src._backRefs;
        _ignorableProps = src._ignorableProps;
        _ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown;
        _anySetter = src._anySetter;
        _injectables = src._injectables;
        _objectIdReader = src._objectIdReader;

        _nonStandardCreation = src._nonStandardCreation;
        UnwrappedPropertyHandler uph = src._unwrappedPropertyHandler;

        if (unwrapper != null) {
            // delegate further unwraps, if any
            if (uph != null) { // got handler, delegate
                uph = uph.renameAll(unwrapper);
            }
            // and handle direct unwrapping as well:
            _beanProperties = src._beanProperties.renameAll(unwrapper);
        } else {
            _beanProperties = src._beanProperties;
        }
        _unwrappedPropertyHandler = uph;
        _needViewProcesing = src._needViewProcesing;
        _serializationShape = src._serializationShape;

        // probably adds a twist, so:
        _vanillaProcessing = false;
    }

    public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir)
    {
        super(src._beanType);
        _beanType = src._beanType;
        
        _valueInstantiator = src._valueInstantiator;
        _delegateDeserializer = src._delegateDeserializer;
        _propertyBasedCreator = src._propertyBasedCreator;
        
        _backRefs = src._backRefs;
        _ignorableProps = src._ignorableProps;
        _ignoreAllUnknown = src._ignoreAllUnknown;
        _anySetter = src._anySetter;
        _injectables = src._injectables;
        
        _nonStandardCreation = src._nonStandardCreation;
        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
        _needViewProcesing = src._needViewProcesing;
        _serializationShape = src._serializationShape;

        // then actual changes:
        _objectIdReader = oir;

        if (oir == null) {
            _beanProperties = src._beanProperties;
            _vanillaProcessing = src._vanillaProcessing;
        } else {
            /* 18-Nov-2012, tatu: May or may not have annotations for id property;
             *   but no easy access. But hard to see id property being optional,
             *   so let's consider required at this point.
             */
            ObjectIdValueProperty idProp = new ObjectIdValueProperty(oir, PropertyMetadata.STD_REQUIRED);
            _beanProperties = src._beanProperties.withProperty(idProp);
            _vanillaProcessing = false;
        }
    }

    public BeanDeserializerBase(BeanDeserializerBase src, Set ignorableProps)
    {
        super(src._beanType);
        _beanType = src._beanType;
        
        _valueInstantiator = src._valueInstantiator;
        _delegateDeserializer = src._delegateDeserializer;
        _propertyBasedCreator = src._propertyBasedCreator;

        _backRefs = src._backRefs;
        _ignorableProps = ignorableProps;
        _ignoreAllUnknown = src._ignoreAllUnknown;
        _anySetter = src._anySetter;
        _injectables = src._injectables;

        _nonStandardCreation = src._nonStandardCreation;
        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
        _needViewProcesing = src._needViewProcesing;
        _serializationShape = src._serializationShape;

        _vanillaProcessing = src._vanillaProcessing;
        _objectIdReader = src._objectIdReader;

        // 01-May-2016, tatu: [databind#1217]: Remove properties from mapping,
        //    to avoid them being deserialized
        _beanProperties = src._beanProperties.withoutProperties(ignorableProps);
    }

    /**
     * @since 2.8
     */
    protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanProps)
    {
        super(src._beanType);
        _beanType = src._beanType;

        _valueInstantiator = src._valueInstantiator;
        _delegateDeserializer = src._delegateDeserializer;
        _propertyBasedCreator = src._propertyBasedCreator;

        _beanProperties = beanProps;
        _backRefs = src._backRefs;
        _ignorableProps = src._ignorableProps;
        _ignoreAllUnknown = src._ignoreAllUnknown;
        _anySetter = src._anySetter;
        _injectables = src._injectables;
        _objectIdReader = src._objectIdReader;
        
        _nonStandardCreation = src._nonStandardCreation;
        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
        _needViewProcesing = src._needViewProcesing;
        _serializationShape = src._serializationShape;

        _vanillaProcessing = src._vanillaProcessing;
    }
    
    @Override
    public abstract JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper);

    public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir);

    public abstract BeanDeserializerBase withIgnorableProperties(Set ignorableProps);

    /**
     * Mutant factory method that custom sub-classes must override; not left as
     * abstract to prevent more drastic backwards compatibility problems.
     *
     * @since 2.8
     */
    public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
        throw new UnsupportedOperationException("Class "+getClass().getName()
                +" does not override `withBeanProperties()`, needs to");
    }
    
    /**
     * Fluent factory for creating a variant that can handle
     * POJO output as a JSON Array. Implementations may ignore this request
     * if no such input is possible.
     *
     * @since 2.1
     */
    protected abstract BeanDeserializerBase asArrayDeserializer();

    /*
    /**********************************************************
    /* Validation, post-processing
    /**********************************************************
     */

    /**
     * Method called to finalize setup of this deserializer,
     * after deserializer itself has been registered.
     * This is needed to handle recursive and transitive dependencies.
     */
    @Override
    public void resolve(DeserializationContext ctxt) throws JsonMappingException
    {
        ExternalTypeHandler.Builder extTypes = null;
        // if ValueInstantiator can use "creator" approach, need to resolve it here...
        SettableBeanProperty[] creatorProps;

        if (_valueInstantiator.canCreateFromObjectWith()) {
            creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());

            // 22-Jan-2018, tatu: May need to propagate "ignorable" status (from `Access.READ_ONLY`
            //     or perhaps class-ignorables) into Creator properties too. Can not just delete,
            //     at this point, but is needed for further processing down the line
            if (_ignorableProps != null) {
                for (int i = 0, end = creatorProps.length; i < end; ++i) {
                    SettableBeanProperty prop  = creatorProps[i];
                    if (_ignorableProps.contains(prop.getName())) {
                        creatorProps[i].markAsIgnorable();
                    }
                }
            }
        } else {
            creatorProps = null;
        }
        UnwrappedPropertyHandler unwrapped = null;

        // 24-Mar-2017, tatu: Looks like we may have to iterate over
        //   properties twice, to handle potential issues with recursive
        //   types (see [databind#1575] f.ex).
        // First loop: find deserializer if not yet known, but do not yet
        // contextualize (since that can lead to problems with self-references)
        // 22-Jan-2018, tatu: NOTE! Need not check for `isIgnorable` as that can
        //   only happen for props in `creatorProps`

        for (SettableBeanProperty prop : _beanProperties) {
            if (!prop.hasValueDeserializer()) {
                // [databind#125]: allow use of converters
                JsonDeserializer deser = findConvertingDeserializer(ctxt, prop);
                if (deser == null) {
                    deser = ctxt.findNonContextualValueDeserializer(prop.getType());
                }
                SettableBeanProperty newProp = prop.withValueDeserializer(deser);
                _replaceProperty(_beanProperties, creatorProps, prop, newProp);
            }
        }

        // Second loop: contextualize, find other pieces
        for (SettableBeanProperty origProp : _beanProperties) {
            SettableBeanProperty prop = origProp;
            JsonDeserializer deser = prop.getValueDeserializer();
            deser = ctxt.handlePrimaryContextualization(deser, prop, prop.getType());
            prop = prop.withValueDeserializer(deser);
            // Need to link managed references with matching back references
            prop = _resolveManagedReferenceProperty(ctxt, prop);

            // [databind#351]: need to wrap properties that require object id resolution.
            if (!(prop instanceof ManagedReferenceProperty)) {
                prop = _resolvedObjectIdProperty(ctxt, prop);
            }
            // Support unwrapped values (via @JsonUnwrapped)
            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;
                }
            }

            // 26-Oct-2016, tatu: Need to have access to value deserializer to know if
            //   merging needed, and now seems to be reasonable time to do that.
            final PropertyMetadata md = prop.getMetadata();
            prop = _resolveMergeAndNullSettings(ctxt, prop, md);

            // non-static inner classes too:
            prop = _resolveInnerClassValuedProperty(ctxt, prop);
            if (prop != origProp) {
                _replaceProperty(_beanProperties, creatorProps, origProp, prop);
            }

            // one more thing: if this property uses "external property" type inclusion,
            // it needs different handling altogether
            if (prop.hasValueTypeDeserializer()) {
                TypeDeserializer typeDeser = prop.getValueTypeDeserializer();
                if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
                    if (extTypes == null) {
                        extTypes = ExternalTypeHandler.builder(_beanType);
                    }
                    extTypes.addExternal(prop, typeDeser);
                    // In fact, remove from list of known properties to simplify later handling
                    _beanProperties.remove(prop);
                    continue;
                }
            }
        }
        // "any setter" may also need to be resolved now
        if ((_anySetter != null) && !_anySetter.hasValueDeserializer()) {
            _anySetter = _anySetter.withValueDeserializer(findDeserializer(ctxt,
                    _anySetter.getType(), _anySetter.getProperty()));
        }
        // as well as delegate-based constructor:
        if (_valueInstantiator.canCreateUsingDelegate()) {
            JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
            if (delegateType == null) {
                ctxt.reportBadDefinition(_beanType, String.format(
"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
                    _beanType, _valueInstantiator.getClass().getName()));
            }
            _delegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
                    _valueInstantiator.getDelegateCreator());
        }

        // and array-delegate-based constructor:
        if (_valueInstantiator.canCreateUsingArrayDelegate()) {
            JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
            if (delegateType == null) {
                ctxt.reportBadDefinition(_beanType, String.format(
"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
                        _beanType, _valueInstantiator.getClass().getName()));
            }
            _arrayDelegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
                    _valueInstantiator.getArrayDelegateCreator());
        }

        // And now that we know CreatorProperty instances are also resolved can finally create the creator:
        if (creatorProps != null) {
            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator,
                    creatorProps, _beanProperties);
        }

        if (extTypes != null) {
            // 21-Jun-2016, tatu: related to [databind#999], may need to link type ids too,
            //    so need to pass collected properties
            _externalTypeIdHandler = extTypes.build(_beanProperties);
            // we consider this non-standard, to offline handling
            _nonStandardCreation = true;
        }
        
        _unwrappedPropertyHandler = unwrapped;
        if (unwrapped != null) { // we consider this non-standard, to offline handling
            _nonStandardCreation = true;
        }
        // may need to disable vanilla processing, if unwrapped handling was enabled...
        _vanillaProcessing = _vanillaProcessing && !_nonStandardCreation;
    }

    /**
     * @since 2.8.8
     */
    protected void _replaceProperty(BeanPropertyMap props, SettableBeanProperty[] creatorProps,
            SettableBeanProperty origProp, SettableBeanProperty newProp)
    {
        props.replace(origProp, newProp);
        // [databind#795]: Make sure PropertyBasedCreator's properties stay in sync
        if (creatorProps != null) {
            // 18-May-2015, tatu: _Should_ start with consistent set. But can we really
            //   fully count on this? May need to revisit in future; seems to hold for now.
            for (int i = 0, len = creatorProps.length; i < len; ++i) {
                if (creatorProps[i] == origProp) {
                    creatorProps[i] = newProp;
                    return;
                }
            }
            /*
            // ... as per above, it is possible we'd need to add this as fallback
            // if (but only if) identity check fails?
            for (int i = 0, len = creatorProps.length; i < len; ++i) {
                if (creatorProps[i].getName().equals(origProp.getName())) {
                    creatorProps[i] = newProp;
                    return;
                }
            }
            */
        }
    }

    @SuppressWarnings("unchecked")
    private JsonDeserializer _findDelegateDeserializer(DeserializationContext ctxt,
            JavaType delegateType, AnnotatedWithParams delegateCreator) throws JsonMappingException
    {
        // Need to create a temporary property to allow contextual deserializers:
        BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME,
                delegateType, null, delegateCreator,
                PropertyMetadata.STD_OPTIONAL);
        TypeDeserializer td = delegateType.getTypeHandler();
        if (td == null) {
            td = ctxt.getConfig().findTypeDeserializer(delegateType);
        }
        // 04-May-2018, tatu: [databind#2021] check if there's custom deserializer attached
        //    to type (resolved from parameter)
        JsonDeserializer dd = delegateType.getValueHandler();
        if (dd == null) {
            dd = findDeserializer(ctxt, delegateType, property);
        } else {
            dd = (JsonDeserializer) ctxt.handleSecondaryContextualization(dd, property, delegateType);
        }
        if (td != null) {
            td = td.forProperty(property);
            return new TypeWrappedDeserializer(td, dd);
        }
        return dd;
    }

    /**
     * Helper method that can be used to see if specified property is annotated
     * to indicate use of a converter for property value (in case of container types,
     * it is container type itself, not key or content type).
     *

* NOTE: returned deserializer is NOT yet contextualized, caller needs to take * care to do that. * * @since 2.2 */ protected JsonDeserializer findConvertingDeserializer(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (intr != null) { Object convDef = intr.findDeserializationConverter(prop.getMember()); if (convDef != null) { Converter conv = ctxt.converterInstance(prop.getMember(), convDef); JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); // 25-Mar-2017, tatu: should not yet contextualize // JsonDeserializer deser = ctxt.findContextualValueDeserializer(delegateType, prop); JsonDeserializer deser = ctxt.findNonContextualValueDeserializer(delegateType); return new StdDelegatingDeserializer(conv, delegateType, deser); } } return null; } /** * Although most of post-processing is done in resolve(), we only get * access to referring property's annotations here; and this is needed * to support per-property ObjectIds. * We will also consider Shape transformations (read from Array) at this * point, since it may come from either Class definition or property. */ @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { ObjectIdReader oir = _objectIdReader; // First: may have an override for Object Id: final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); final AnnotatedMember accessor = _neitherNull(property, intr) ? property.getMember() : null; if (accessor != null) { ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) // 2.1: allow modifications by "id ref" annotations as well: objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); Class implClass = objectIdInfo.getGeneratorType(); // Property-based generator is trickier JavaType idType; SettableBeanProperty idProp; ObjectIdGenerator idGen; ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); if (implClass == ObjectIdGenerators.PropertyGenerator.class) { PropertyName propName = objectIdInfo.getPropertyName(); idProp = findProperty(propName); if (idProp == null) { ctxt.reportBadDefinition(_beanType, String.format( "Invalid Object Id definition for %s: cannot find property with name '%s'", handledType().getName(), propName)); } idType = idProp.getType(); idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); } else { // other types are to be simpler JavaType type = ctxt.constructType(implClass); idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; idProp = null; idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo); } JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), idGen, deser, idProp, resolver); } } // either way, need to resolve serializer: BeanDeserializerBase contextual = this; if (oir != null && oir != _objectIdReader) { contextual = contextual.withObjectIdReader(oir); } // And possibly add more properties to ignore if (accessor != null) { JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(accessor); if (ignorals != null) { Set ignored = ignorals.findIgnoredForDeserialization(); if (!ignored.isEmpty()) { Set prev = contextual._ignorableProps; if ((prev != null) && !prev.isEmpty()) { ignored = new HashSet(ignored); ignored.addAll(prev); } contextual = contextual.withIgnorableProperties(ignored); } } } // One more thing: are we asked to serialize POJO as array? JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); JsonFormat.Shape shape = null; if (format != null) { if (format.hasShape()) { shape = format.getShape(); } // 16-May-2016, tatu: How about per-property case-insensitivity? Boolean B = format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); if (B != null) { BeanPropertyMap propsOrig = _beanProperties; BeanPropertyMap props = propsOrig.withCaseInsensitivity(B.booleanValue()); if (props != propsOrig) { contextual = contextual.withBeanProperties(props); } } } if (shape == null) { shape = _serializationShape; } if (shape == JsonFormat.Shape.ARRAY) { contextual = contextual.asArrayDeserializer(); } return contextual; } /** * Helper method called to see if given property is part of 'managed' property * pair (managed + back reference), and if so, handle resolution details. */ protected SettableBeanProperty _resolveManagedReferenceProperty(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { String refName = prop.getManagedReferenceName(); if (refName == null) { return prop; } JsonDeserializer valueDeser = prop.getValueDeserializer(); SettableBeanProperty backProp = valueDeser.findBackReference(refName); if (backProp == null) { ctxt.reportBadDefinition(_beanType, String.format( "Cannot handle managed/back reference '%s': no back reference property found from type %s", refName, prop.getType())); } // also: verify that type is compatible JavaType referredType = _beanType; JavaType backRefType = backProp.getType(); boolean isContainer = prop.getType().isContainerType(); if (!backRefType.getRawClass().isAssignableFrom(referredType.getRawClass())) { ctxt.reportBadDefinition(_beanType, String.format( "Cannot handle managed/back reference '%s': back reference type (%s) not compatible with managed type (%s)", refName, backRefType.getRawClass().getName(), referredType.getRawClass().getName())); } return new ManagedReferenceProperty(prop, refName, backProp, isContainer); } /** * Method that wraps given property with {@link ObjectIdReferenceProperty} * in case where object id resolution is required. */ protected SettableBeanProperty _resolvedObjectIdProperty(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { ObjectIdInfo objectIdInfo = prop.getObjectIdInfo(); JsonDeserializer valueDeser = prop.getValueDeserializer(); ObjectIdReader objectIdReader = (valueDeser == null) ? null : valueDeser.getObjectIdReader(); if (objectIdInfo == null && objectIdReader == null) { return prop; } return new ObjectIdReferenceProperty(prop, objectIdInfo); } /** * Helper method called to see if given property might be so-called unwrapped * property: these require special handling. */ protected NameTransformer _findPropertyUnwrapper(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { AnnotatedMember am = prop.getMember(); if (am != null) { NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(am); if (unwrapper != null) { // 01-Dec-2016, tatu: As per [databind#265] we cannot yet support passing // of unwrapped values through creator properties, so fail fast if (prop instanceof CreatorProperty) { ctxt.reportBadDefinition(getValueType(), String.format( "Cannot define Creator property \"%s\" as `@JsonUnwrapped`: combination not yet supported", prop.getName())); } return unwrapper; } } return null; } /** * Helper method that will handle gruesome details of dealing with properties * that have non-static inner class as value... */ protected SettableBeanProperty _resolveInnerClassValuedProperty(DeserializationContext ctxt, SettableBeanProperty prop) { /* Should we encounter a property that has non-static inner-class * as value, we need to add some more magic to find the "hidden" constructor... */ JsonDeserializer deser = prop.getValueDeserializer(); // ideally wouldn't rely on it being BeanDeserializerBase; but for now it'll have to do if (deser instanceof BeanDeserializerBase) { BeanDeserializerBase bd = (BeanDeserializerBase) deser; ValueInstantiator vi = bd.getValueInstantiator(); if (!vi.canCreateUsingDefault()) { // no default constructor Class valueClass = prop.getType().getRawClass(); // NOTE: almost same as `isNonStaticInnerClass()` but need to know enclosing... Class enclosing = ClassUtil.getOuterClass(valueClass); // and is inner class of the bean class... if ((enclosing != null) && (enclosing == _beanType.getRawClass())) { for (Constructor ctor : valueClass.getConstructors()) { Class[] paramTypes = ctor.getParameterTypes(); if (paramTypes.length == 1) { if (enclosing.equals(paramTypes[0])) { if (ctxt.canOverrideAccessModifiers()) { ClassUtil.checkAndFixAccess(ctor, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } return new InnerClassProperty(prop, ctor); } } } } } } return prop; } // @since 2.9 protected SettableBeanProperty _resolveMergeAndNullSettings(DeserializationContext ctxt, SettableBeanProperty prop, PropertyMetadata propMetadata) throws JsonMappingException { PropertyMetadata.MergeInfo merge = propMetadata.getMergeInfo(); // First mergeability if (merge != null) { JsonDeserializer valueDeser = prop.getValueDeserializer(); Boolean mayMerge = valueDeser.supportsUpdate(ctxt.getConfig()); if (mayMerge == null) { // we don't really know if it's ok; so only use if explicitly specified if (merge.fromDefaults) { return prop; } } else if (!mayMerge.booleanValue()) { // prevented if (!merge.fromDefaults) { // If attempts was made via explicit annotation/per-type config override, // should be reported; may or may not result in exception ctxt.reportBadMerge(valueDeser); } return prop; } // Anyway; if we get this far, do enable merging AnnotatedMember accessor = merge.getter; accessor.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); if (!(prop instanceof SetterlessProperty)) { prop = MergingSettableBeanProperty.construct(prop, accessor); } } // And after this, see if we require non-standard null handling NullValueProvider nuller = findValueNullProvider(ctxt, prop, propMetadata); if (nuller != null) { prop = prop.withNullProvider(nuller); } return prop; } /* /********************************************************** /* Public accessors; null/empty value providers /********************************************************** */ @Override public AccessPattern getNullAccessPattern() { // POJO types do not have custom `null` values return AccessPattern.ALWAYS_NULL; } @Override public AccessPattern getEmptyAccessPattern() { // Empty values cannot be shared return AccessPattern.DYNAMIC; } @Override // since 2.9 public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { // alas, need to promote exception, if any: try { return _valueInstantiator.createUsingDefault(ctxt); } catch (IOException e) { return ClassUtil.throwAsMappingException(ctxt, e); } } /* /********************************************************** /* Public accessors; other /********************************************************** */ @Override public boolean isCachable() { return true; } @Override // since 2.9 public Boolean supportsUpdate(DeserializationConfig config) { // although with possible caveats, yes, values can be updated // 23-Oct-2016, tatu: Perhaps in future could and should verify from // bean settings... return Boolean.TRUE; } @Override public Class handledType() { return _beanType.getRawClass(); } /** * Overridden to return true for those instances that are * handling value for which Object Identity handling is enabled * (either via value type or referring property). */ @Override public ObjectIdReader getObjectIdReader() { return _objectIdReader; } public boolean hasProperty(String propertyName) { return _beanProperties.find(propertyName) != null; } public boolean hasViews() { return _needViewProcesing; } /** * Accessor for checking number of deserialized properties. */ public int getPropertyCount() { return _beanProperties.size(); } @Override public Collection getKnownPropertyNames() { ArrayList names = new ArrayList(); for (SettableBeanProperty prop : _beanProperties) { names.add(prop.getName()); } return names; } /** * @deprecated Since 2.3, use {@link #handledType()} instead */ @Deprecated public final Class getBeanClass() { return _beanType.getRawClass(); } @Override public JavaType getValueType() { return _beanType; } /** * Accessor for iterating over properties this deserializer uses; with * the exception that properties passed via Creator methods * (specifically, "property-based constructor") are not included, * but can be accessed separate by calling * {@link #creatorProperties} */ public Iterator properties() { if (_beanProperties == null) { throw new IllegalStateException("Can only call after BeanDeserializer has been resolved"); } return _beanProperties.iterator(); } /** * Accessor for finding properties that represents values to pass * through property-based creator method (constructor or * factory method) * * @since 2.0 */ public Iterator creatorProperties() { if (_propertyBasedCreator == null) { return Collections.emptyList().iterator(); } return _propertyBasedCreator.properties().iterator(); } public SettableBeanProperty findProperty(PropertyName propertyName) { // TODO: start matching full name? return findProperty(propertyName.getSimpleName()); } /** * Accessor for finding the property with given name, if POJO * has one. Name used is the external name, i.e. name used * in external data representation (JSON). * * @since 2.0 */ public SettableBeanProperty findProperty(String propertyName) { SettableBeanProperty prop = (_beanProperties == null) ? null : _beanProperties.find(propertyName); if (prop == null && _propertyBasedCreator != null) { prop = _propertyBasedCreator.findCreatorProperty(propertyName); } return prop; } /** * Alternate find method that tries to locate a property with given * property index. * Note that access by index is not necessarily faster than by name, * since properties are not directly indexable; however, for most * instances difference is not significant as number of properties * is low. * * @since 2.3 */ public SettableBeanProperty findProperty(int propertyIndex) { SettableBeanProperty prop = (_beanProperties == null) ? null : _beanProperties.find(propertyIndex); if (prop == null && _propertyBasedCreator != null) { prop = _propertyBasedCreator.findCreatorProperty(propertyIndex); } return prop; } /** * Method needed by {@link BeanDeserializerFactory} to properly link * managed- and back-reference pairs. */ @Override public SettableBeanProperty findBackReference(String logicalName) { if (_backRefs == null) { return null; } return _backRefs.get(logicalName); } @Override // ValueInstantiator.Gettable public ValueInstantiator getValueInstantiator() { return _valueInstantiator; } /* /********************************************************** /* Mutators /********************************************************** */ /** * Method that can be used to replace an existing property with * a modified one. *

* NOTE: only ever use this method if you know what you are doing; * incorrect usage can break deserializer. * * @param original Property to replace * @param replacement Property to replace it with * * @since 2.1 */ public void replaceProperty(SettableBeanProperty original, SettableBeanProperty replacement) { _beanProperties.replace(original, replacement); } /* /********************************************************** /* Partial deserializer implementation /********************************************************** */ /** * General version used when handling needs more advanced * features. */ public abstract Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException; @Override public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { // 16-Feb-2012, tatu: ObjectId may be used as well... need to check that first if (_objectIdReader != null) { // 05-Aug-2013, tatu: May use native Object Id if (p.canReadObjectId()) { Object id = p.getObjectId(); if (id != null) { Object ob = typeDeserializer.deserializeTypedFromObject(p, ctxt); return _handleTypedObjectId(p, ctxt, ob, id); } } // or, Object Ids Jackson explicitly sets JsonToken t = p.getCurrentToken(); if (t != null) { // Most commonly, a scalar (int id, uuid String, ...) if (t.isScalarValue()) { return deserializeFromObjectId(p, ctxt); } // but, with 2.5+, a simple Object-wrapped value also legal: if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { return deserializeFromObjectId(p, ctxt); } } } // In future could check current token... for now this should be enough: return typeDeserializer.deserializeTypedFromObject(p, ctxt); } /** * Offlined method called to handle "native" Object Id that has been read * and known to be associated with given deserialized POJO. * * @since 2.3 */ protected Object _handleTypedObjectId(JsonParser p, DeserializationContext ctxt, Object pojo, Object rawId) throws IOException { // One more challenge: type of id may not be type of property we are expecting // later on; specifically, numeric ids vs Strings. JsonDeserializer idDeser = _objectIdReader.getDeserializer(); final Object id; // Ok, this is bit ridiculous; let's see if conversion is needed: if (idDeser.handledType() == rawId.getClass()) { // nope: already same type id = rawId; } else { id = _convertObjectId(p, ctxt, rawId, idDeser); } ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); roid.bindItem(pojo); // also: may need to set a property value as well SettableBeanProperty idProp = _objectIdReader.idProperty; if (idProp != null) { return idProp.setAndReturn(pojo, id); } return pojo; } /** * Helper method we need to do necessary conversion from whatever native object id * type is, into declared type that Jackson internals expect. This may be * simple cast (for String ids), or something more complicated; in latter * case we may need to create bogus content buffer to allow use of * id deserializer. * * @since 2.3 */ @SuppressWarnings("resource") // TokenBuffers don't need close, nor parser thereof protected Object _convertObjectId(JsonParser p, DeserializationContext ctxt, Object rawId, JsonDeserializer idDeser) throws IOException { TokenBuffer buf = new TokenBuffer(p, ctxt); if (rawId instanceof String) { buf.writeString((String) rawId); } else if (rawId instanceof Long) { buf.writeNumber(((Long) rawId).longValue()); } else if (rawId instanceof Integer) { buf.writeNumber(((Integer) rawId).intValue()); } else { // should we worry about UUIDs? They should be fine, right? // 07-Aug-2014, tatu: Maybe, but not necessarily; had issues with // Smile format; [dataformat-smile#19], possibly related. // 01-Sep-2016, tatu: For non-JSON, might want to consider `writeEmbeddedObject` // but that won't work for default impl (JSON and most dataformats) buf.writeObject(rawId); } JsonParser bufParser = buf.asParser(); bufParser.nextToken(); return idDeser.deserialize(bufParser, ctxt); } // NOTE: currently only used by standard BeanDeserializer (not Builder-based) /** * Alternative deserialization method used when we expect to see Object Id; * if so, we will need to ensure that the Id is seen before anything * else, to ensure that it is available for solving references, * even if JSON itself is not ordered that way. This may require * buffering in some cases, but usually just a simple lookup to ensure * that ordering is correct. */ protected Object deserializeWithObjectId(JsonParser p, DeserializationContext ctxt) throws IOException { return deserializeFromObject(p, ctxt); } /** * Method called in cases where it looks like we got an Object Id * to parse and use as a reference. */ protected Object deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException { Object id = _objectIdReader.readObjectReference(p, ctxt); ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); // do we have it resolved? Object pojo = roid.resolve(); if (pojo == null) { // not yet; should wait... throw new UnresolvedForwardReference(p, "Could not resolve Object Id ["+id+"] (for "+_beanType+").", p.getCurrentLocation(), roid); } return pojo; } protected Object deserializeFromObjectUsingNonDefault(JsonParser p, DeserializationContext ctxt) throws IOException { final JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { return _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); } if (_propertyBasedCreator != null) { return _deserializeUsingPropertyBased(p, ctxt); } // 25-Jan-2017, tatu: We do not actually support use of Creators for non-static // inner classes -- with one and only one exception; that of default constructor! // -- so let's indicate it Class raw = _beanType.getRawClass(); if (ClassUtil.isNonStaticInnerClass(raw)) { return ctxt.handleMissingInstantiator(raw, null, p, "can only instantiate non-static inner class by using default, no-argument constructor"); } return ctxt.handleMissingInstantiator(raw, getValueInstantiator(), p, "cannot deserialize from Object value (no delegate- or property-based Creator)"); } protected abstract Object _deserializeUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt) throws IOException; public Object deserializeFromNumber(JsonParser p, DeserializationContext ctxt) throws IOException { // First things first: id Object Id is used, most likely that's it if (_objectIdReader != null) { return deserializeFromObjectId(p, ctxt); } final JsonDeserializer delegateDeser = _delegateDeserializer(); NumberType nt = p.getNumberType(); if (nt == NumberType.INT) { if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromInt()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } return _valueInstantiator.createFromInt(ctxt, p.getIntValue()); } if (nt == NumberType.LONG) { if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromInt()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } return _valueInstantiator.createFromLong(ctxt, p.getLongValue()); } // actually, could also be BigInteger, so: if (delegateDeser != null) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p, "no suitable creator method found to deserialize from Number value (%s)", p.getNumberValue()); } public Object deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException { // First things first: id Object Id is used, most likely that's it if (_objectIdReader != null) { return deserializeFromObjectId(p, ctxt); } // Bit complicated if we have delegating creator; may need to use it, // or might not... JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromString()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } return _valueInstantiator.createFromString(ctxt, p.getText()); } /** * Method called to deserialize POJO value from a JSON floating-point * number. */ public Object deserializeFromDouble(JsonParser p, DeserializationContext ctxt) throws IOException { NumberType t = p.getNumberType(); // no separate methods for taking float... if ((t == NumberType.DOUBLE) || (t == NumberType.FLOAT)) { JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromDouble()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } return _valueInstantiator.createFromDouble(ctxt, p.getDoubleValue()); } // actually, could also be BigDecimal, so: JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { return _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); } return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p, "no suitable creator method found to deserialize from Number value (%s)", p.getNumberValue()); } /** * Method called to deserialize POJO value from a JSON boolean value (true, false) */ public Object deserializeFromBoolean(JsonParser p, DeserializationContext ctxt) throws IOException { JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromBoolean()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } boolean value = (p.getCurrentToken() == JsonToken.VALUE_TRUE); return _valueInstantiator.createFromBoolean(ctxt, value); } public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException { // note: cannot call `_delegateDeserializer()` since order reversed here: JsonDeserializer 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(handledType(), JsonToken.START_ARRAY, p, null); } return ctxt.handleUnexpectedToken(handledType(), p); } public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt) throws IOException { // First things first: id Object Id is used, most likely that's it; specifically, // true for UUIDs when written as binary (with Smile, other binary formats) if (_objectIdReader != null) { return deserializeFromObjectId(p, ctxt); } // 26-Jul-2017, tatu: as per [databind#1711] need to support delegating case too JsonDeserializer delegateDeser = _delegateDeserializer(); if (delegateDeser != null) { if (!_valueInstantiator.canCreateFromString()) { Object bean = _valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt)); if (_injectables != null) { injectValues(ctxt, bean); } return bean; } } // TODO: maybe add support for ValueInstantiator, embedded? // 26-Jul-2017, tatu: related to [databind#1711], let's actually verify assignment // compatibility before returning. Bound to catch misconfigured cases and produce // more meaningful exceptions. Object value = p.getEmbeddedObject(); if (value != null) { if (!_beanType.isTypeOrSuperTypeOf(value.getClass())) { // allow this to be handled... value = ctxt.handleWeirdNativeValue(_beanType, value, p); } } return value; } /** * @since 2.9 */ private final JsonDeserializer _delegateDeserializer() { JsonDeserializer deser = _delegateDeserializer; if (deser == null) { deser = _arrayDelegateDeserializer; } return deser; } /* /********************************************************** /* Overridable helper methods /********************************************************** */ protected void injectValues(DeserializationContext ctxt, Object bean) throws IOException { for (ValueInjector injector : _injectables) { injector.inject(ctxt, bean); } } /** * Method called to handle set of one or more unknown properties, * stored in their entirety in given {@link TokenBuffer} * (as field entries, name and value). */ @SuppressWarnings("resource") protected Object handleUnknownProperties(DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens) throws IOException { // First: add closing END_OBJECT as marker unknownTokens.writeEndObject(); // note: buffer does NOT have starting START_OBJECT JsonParser bufferParser = unknownTokens.asParser(); while (bufferParser.nextToken() != JsonToken.END_OBJECT) { String propName = bufferParser.getCurrentName(); // Unknown: let's call handler method bufferParser.nextToken(); handleUnknownProperty(bufferParser, ctxt, bean, propName); } return bean; } /** * Helper method called for an unknown property, when using "vanilla" * processing. */ protected void handleUnknownVanilla(JsonParser p, DeserializationContext ctxt, Object bean, String propName) throws IOException { if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); } else if (_anySetter != null) { try { // should we consider return type of any setter? _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } } else { // Unknown: let's call handler method handleUnknownProperty(p, ctxt, bean, propName); } } /** * Method called when a JSON property is encountered that has not matching * setter, any-setter or field, and thus cannot be assigned. */ @Override protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException { if (_ignoreAllUnknown) { p.skipChildren(); return; } if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, beanOrClass, propName); } // Otherwise use default handling (call handler(s); if not // handled, throw exception or skip depending on settings) super.handleUnknownProperty(p, ctxt, beanOrClass, propName); } /** * Method called when an explicitly ignored property (one specified with a * name to match, either by property annotation or class annotation) is encountered. * * @since 2.3 */ protected void handleIgnoredProperty(JsonParser p, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException { if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)) { throw IgnoredPropertyException.from(p, beanOrClass, propName, getKnownPropertyNames()); } p.skipChildren(); } /** * Method called in cases where we may have polymorphic deserialization * case: that is, type of Creator-constructed bean is not the type * of deserializer itself. It should be a sub-class or implementation * class; either way, we may have more specific deserializer to use * for handling it. * * @param p (optional) If not null, parser that has more properties to handle * (in addition to buffered properties); if null, all properties are passed * in buffer */ @SuppressWarnings("resource") protected Object handlePolymorphic(JsonParser p, DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens) throws IOException { // First things first: maybe there is a more specific deserializer available? JsonDeserializer subDeser = _findSubclassDeserializer(ctxt, bean, unknownTokens); if (subDeser != null) { if (unknownTokens != null) { // need to add END_OBJECT marker first unknownTokens.writeEndObject(); JsonParser p2 = unknownTokens.asParser(); p2.nextToken(); // to get to first data field bean = subDeser.deserialize(p2, ctxt, bean); } // Original parser may also have some leftovers if (p != null) { bean = subDeser.deserialize(p, ctxt, bean); } return bean; } // nope; need to use this deserializer. Unknowns we've seen so far? if (unknownTokens != null) { bean = handleUnknownProperties(ctxt, bean, unknownTokens); } // and/or things left to process via main parser? if (p != null) { bean = deserialize(p, ctxt, bean); } return bean; } /** * Helper method called to (try to) locate deserializer for given sub-type of * type that this deserializer handles. */ protected JsonDeserializer _findSubclassDeserializer(DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens) throws IOException { JsonDeserializer subDeser; // First: maybe we have already created sub-type deserializer? synchronized (this) { subDeser = (_subDeserializers == null) ? null : _subDeserializers.get(new ClassKey(bean.getClass())); } if (subDeser != null) { return subDeser; } // If not, maybe we can locate one. First, need provider JavaType type = ctxt.constructType(bean.getClass()); /* 30-Jan-2012, tatu: Ideally we would be passing referring * property; which in theory we could keep track of via * ResolvableDeserializer (if we absolutely must...). * But for now, let's not bother. */ // subDeser = ctxt.findValueDeserializer(type, _property); subDeser = ctxt.findRootValueDeserializer(type); // Also, need to cache it if (subDeser != null) { synchronized (this) { if (_subDeserializers == null) { _subDeserializers = new HashMap>();; } _subDeserializers.put(new ClassKey(bean.getClass()), subDeser); } } return subDeser; } /* /********************************************************** /* Helper methods for error reporting /********************************************************** */ /** * Method that will modify caught exception (passed in as argument) * as necessary to include reference information, and to ensure it * is a subtype of {@link IOException}, or an unchecked exception. *

* Rules for wrapping and unwrapping are bit complicated; essentially: *

    *
  • Errors are to be passed as is (if uncovered via unwrapping) *
  • "Plain" IOExceptions (ones that are not of type * {@link JsonMappingException} are to be passed as is *
*/ public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) throws IOException { // Need to add reference information throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName); } private Throwable throwOrReturnThrowable(Throwable t, DeserializationContext ctxt) throws IOException { /* 05-Mar-2009, tatu: But one nasty edge is when we get * StackOverflow: usually due to infinite loop. But that * often gets hidden within an InvocationTargetException... */ while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors to be passed as is ClassUtil.throwIfError(t); boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); // Ditto for IOExceptions; except we may want to wrap JSON exceptions if (t instanceof IOException) { if (!wrap || !(t instanceof JsonProcessingException)) { throw (IOException) t; } } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions ClassUtil.throwIfRTE(t); } return t; } protected Object wrapInstantiationProblem(Throwable t, DeserializationContext ctxt) throws IOException { while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors and "plain" IOExceptions to be passed as is ClassUtil.throwIfError(t); if (t instanceof IOException) { // Since we have no more information to add, let's not actually wrap.. throw (IOException) t; } boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions ClassUtil.throwIfRTE(t); } return ctxt.handleInstantiationProblem(_beanType.getRawClass(), null, t); } }