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.JsonFormat; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 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 <code>BeanDeserializer</code>. */ public abstract class BeanDeserializerBase extends StdDeserializer<Object> implements ContextualDeserializer, ResolvableDeserializer, java.io.Serializable // since 2.1 { private static final long serialVersionUID = -2038793552422727904L; /* /********************************************************** /* Information regarding type being deserialized /********************************************************** */ /** * Annotations from the bean class: used for accessing * annotations during resolution * (see {@link #resolve}) and * contextualization (see {@link #createContextual}) *<p> * Transient since annotations only used during construction. */ final private transient Annotations _classAnnotations; /** * 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<Object> _delegateDeserializer; /** * 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 HashSet<String> _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<String, SettableBeanProperty> _backRefs; /* /********************************************************** /* Related handlers /********************************************************** */ /** * Lazily constructed map used to contain deserializers needed * for polymorphic subtypes. * Note that this is <b>only needed</b> for polymorphic types, * that is, when the actual type is not statically known. * For other types this remains null. */ protected transient HashMap<ClassKey, JsonDeserializer<Object>> _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<String, SettableBeanProperty> backRefs, HashSet<String> ignorableProps, boolean ignoreAllUnknown, boolean hasViews) { super(beanDesc.getType()); AnnotatedClass ac = beanDesc.getClassInfo(); _classAnnotations = ac.getAnnotations(); _beanType = beanDesc.getType(); _valueInstantiator = builder.getValueInstantiator(); _beanProperties = properties; _backRefs = backRefs; _ignorableProps = ignorableProps; _ignoreAllUnknown = ignoreAllUnknown; _anySetter = builder.getAnySetter(); List<ValueInjector> injectables = builder.getInjectables(); _injectables = (injectables == null || injectables.isEmpty()) ? null : injectables.toArray(new ValueInjector[injectables.size()]); _objectIdReader = builder.getObjectIdReader(); _nonStandardCreation = (_unwrappedPropertyHandler != null) || _valueInstantiator.canCreateUsingDelegate() || _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); _classAnnotations = src._classAnnotations; _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); _classAnnotations = src._classAnnotations; _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 = src._unwrappedPropertyHandler; if (unwrapper != null) { // delegate further unwraps, if any if (_unwrappedPropertyHandler != null) { // got handler, delegate _unwrappedPropertyHandler.renameAll(unwrapper); } // and handle direct unwrapping as well: _beanProperties = src._beanProperties.renameAll(unwrapper); } else { _beanProperties = src._beanProperties; } _needViewProcesing = src._needViewProcesing; _serializationShape = src._serializationShape; // probably adds a twist, so: _vanillaProcessing = false; } public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir) { super(src._beanType); _classAnnotations = src._classAnnotations; _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; _vanillaProcessing = src._vanillaProcessing; // then actual changes: _objectIdReader = oir; if (oir == null) { _beanProperties = src._beanProperties; } else { _beanProperties = src._beanProperties.withProperty(new ObjectIdValueProperty(oir)); } } public BeanDeserializerBase(BeanDeserializerBase src, HashSet<String> ignorableProps) { super(src._beanType); _classAnnotations = src._classAnnotations; _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; _beanProperties = src._beanProperties; } @Override public abstract JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper); public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir); public abstract BeanDeserializerBase withIgnorableProperties(HashSet<String> ignorableProps); /** * 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... if (_valueInstantiator.canCreateFromObjectWith()) { SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps); // also: need to try to resolve 'external' type ids... for (SettableBeanProperty prop : _propertyBasedCreator.properties()) { if (prop.hasValueTypeDeserializer()) { TypeDeserializer typeDeser = prop.getValueTypeDeserializer(); if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) { if (extTypes == null) { extTypes = new ExternalTypeHandler.Builder(); } extTypes.addExternal(prop, typeDeser.getPropertyName()); } } } } UnwrappedPropertyHandler unwrapped = null; for (SettableBeanProperty origProp : _beanProperties) { SettableBeanProperty prop = origProp; // May already have deserializer from annotations, if so, skip: if (!prop.hasValueDeserializer()) { prop = prop.withValueDeserializer(findDeserializer(ctxt, prop.getType(), prop)); } else { // may need contextual version JsonDeserializer<Object> deser = prop.getValueDeserializer(); if (deser instanceof ContextualDeserializer) { JsonDeserializer<?> cd = ((ContextualDeserializer) deser).createContextual(ctxt, prop); if (cd != deser) { prop = prop.withValueDeserializer(cd); } } } // [JACKSON-235]: need to link managed references with matching back references prop = _resolveManagedReferenceProperty(ctxt, prop); // [JACKSON-132]: support unwrapped values (via @JsonUnwrapped) SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop); if (u != null) { prop = u; if (unwrapped == null) { unwrapped = new UnwrappedPropertyHandler(); } unwrapped.addProperty(prop); continue; } // [JACKSON-594]: non-static inner classes too: prop = _resolveInnerClassValuedProperty(ctxt, prop); if (prop != origProp) { _beanProperties.replace(prop); } /* one more thing: if this property uses "external property" type inclusion * (see [JACKSON-453]), it needs different handling altogether */ if (prop.hasValueTypeDeserializer()) { TypeDeserializer typeDeser = prop.getValueTypeDeserializer(); if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) { if (extTypes == null) { extTypes = new ExternalTypeHandler.Builder(); } extTypes.addExternal(prop, typeDeser.getPropertyName()); // 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) { throw new IllegalArgumentException("Invalid delegate-creator definition for "+_beanType +": value instantiator ("+_valueInstantiator.getClass().getName() +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'"); } AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator(); // Need to create a temporary property to allow contextual deserializers: BeanProperty.Std property = new BeanProperty.Std(null, delegateType, _classAnnotations, delegateCreator); _delegateDeserializer = findDeserializer(ctxt, delegateType, property); } if (extTypes != null) { _externalTypeIdHandler = extTypes.build(); // 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; } /** * 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; String[] ignorals = null; // First: may have an override for Object Id: final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); final AnnotatedMember accessor = (property == null || intr == null) ? null : property.getMember(); if (property != null && intr != null) { ignorals = intr.findPropertiesToIgnore(accessor); 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; if (implClass == ObjectIdGenerators.PropertyGenerator.class) { String propName = objectIdInfo.getPropertyName(); idProp = findProperty(propName); if (idProp == null) { throw new IllegalArgumentException("Invalid Object Id definition for " +getBeanClass().getName()+": can not find property with name '"+propName+"'"); } idType = idProp.getType(); idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); } else { // other types need 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); } } // 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 (ignorals != null && ignorals.length != 0) { HashSet<String> newIgnored = ArrayBuilders.setAndArray(contextual._ignorableProps, ignorals); contextual = contextual.withIgnorableProperties(newIgnored); } // One more thing: are we asked to serialize POJO as array? JsonFormat.Shape shape = null; if (accessor != null) { JsonFormat.Value format = intr.findFormat((Annotated) accessor); if (format != null) { shape = format.getShape(); } } 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) { String refName = prop.getManagedReferenceName(); if (refName == null) { return prop; } JsonDeserializer<?> valueDeser = prop.getValueDeserializer(); SettableBeanProperty backProp = null; boolean isContainer = false; if (valueDeser instanceof BeanDeserializerBase) { backProp = ((BeanDeserializerBase) valueDeser).findBackReference(refName); } else if (valueDeser instanceof ContainerDeserializerBase<?>) { JsonDeserializer<?> contentDeser = ((ContainerDeserializerBase<?>) valueDeser).getContentDeserializer(); if (!(contentDeser instanceof BeanDeserializerBase)) { String deserName = (contentDeser == null) ? "NULL" : contentDeser.getClass().getName(); throw new IllegalArgumentException("Can not handle managed/back reference '"+refName +"': value deserializer is of type ContainerDeserializerBase, but content type is not handled by a BeanDeserializer " +" (instead it's of type "+deserName+")"); } backProp = ((BeanDeserializerBase) contentDeser).findBackReference(refName); isContainer = true; } else if (valueDeser instanceof AbstractDeserializer) { backProp = ((AbstractDeserializer) valueDeser).findBackReference(refName); } else { throw new IllegalArgumentException("Can not handle managed/back reference '"+refName +"': type for value deserializer is not BeanDeserializer or ContainerDeserializerBase, but " +valueDeser.getClass().getName()); } if (backProp == null) { throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': no back reference property found from type " +prop.getType()); } // also: verify that type is compatible JavaType referredType = _beanType; JavaType backRefType = backProp.getType(); if (!backRefType.getRawClass().isAssignableFrom(referredType.getRawClass())) { throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': back reference type (" +backRefType.getRawClass().getName()+") not compatible with managed type (" +referredType.getRawClass().getName()+")"); } return new ManagedReferenceProperty(prop, refName, backProp, _classAnnotations, isContainer); } /** * Helper method called to see if given property might be so-called unwrapped * property: these require special handling. */ protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext ctxt, SettableBeanProperty prop) { AnnotatedMember am = prop.getMember(); if (am != null) { NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(am); if (unwrapper != null) { JsonDeserializer<Object> orig = prop.getValueDeserializer(); JsonDeserializer<Object> unwrapping = orig.unwrappingDeserializer(unwrapper); if (unwrapping != orig && unwrapping != null) { // might be cleaner to create new instance; but difficult to do reliably, so: return prop.withValueDeserializer(unwrapping); } } } return 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<Object> 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(); 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 && paramTypes[0] == enclosing) { if (ctxt.getConfig().canOverrideAccessModifiers()) { ClassUtil.checkAndFixAccess(ctor); } return new InnerClassProperty(prop, ctor); } } } } } return prop; } /* /********************************************************** /* Public accessors /********************************************************** */ @Override public boolean isCachable() { return true; } /** * 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<Object> getKnownPropertyNames() { ArrayList<Object> names = new ArrayList<Object>(); for (SettableBeanProperty prop : _beanProperties) { names.add(prop.getName()); } return names; } 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<SettableBeanProperty> 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<SettableBeanProperty> creatorProperties() { if (_propertyBasedCreator == null) { return Collections.<SettableBeanProperty>emptyList().iterator(); } return _propertyBasedCreator.properties().iterator(); } /** * 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; } /** * Method needed by {@link BeanDeserializerFactory} to properly link * managed- and back-reference pairs. */ public SettableBeanProperty findBackReference(String logicalName) { if (_backRefs == null) { return null; } return _backRefs.get(logicalName); } public ValueInstantiator getValueInstantiator() { return _valueInstantiator; } /* /********************************************************** /* Mutators /********************************************************** */ /** * Method that can be used to replace an existing property with * a modified one. *<p> * 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(replacement); } /* /********************************************************** /* Partial deserializer implementation /********************************************************** */ @Override public final Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException { /* 16-Feb-2012, tatu: ObjectId may be used as well... need to check * that first */ if (_objectIdReader != null) { JsonToken t = jp.getCurrentToken(); // should be good enough check; we only care about Strings, integral numbers: if (t != null && t.isScalarValue()) { return deserializeFromObjectId(jp, ctxt); } } // In future could check current token... for now this should be enough: return typeDeserializer.deserializeTypedFromObject(jp, 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 jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Object id = _objectIdReader.deserializer.deserialize(jp, ctxt); ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); // do we have it resolved? Object pojo = roid.item; if (pojo == null) { // not yet; should wait... throw new IllegalStateException("Could not resolve Object Id ["+id+"] -- unresolved forward-reference?"); } return pojo; } /* /********************************************************** /* Overridable helper methods /********************************************************** */ protected void injectValues(DeserializationContext ctxt, Object bean) throws IOException, JsonProcessingException { for (ValueInjector injector : _injectables) { injector.inject(ctxt, bean); } } /** * Method called when a JSON property is encountered that has not matching * setter, any-setter or field, and thus can not be assigned. */ @Override protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException, JsonProcessingException { /* 22-Aug-2010, tatu: Caller now mostly checks for ignorable properties, so * following should not be necessary. However, "handleUnknownProperties()" seems * to still possibly need it so it is left for now. */ // If registered as ignorable, skip if (_ignoreAllUnknown || (_ignorableProps != null && _ignorableProps.contains(propName))) { jp.skipChildren(); return; } /* Otherwise use default handling (call handler(s); if not * handled, throw exception or skip depending on settings) */ super.handleUnknownProperty(jp, ctxt, beanOrClass, propName); } /** * 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). */ protected Object handleUnknownProperties(DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens) throws IOException, JsonProcessingException { // 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 to (try to) locate deserializer for given sub-type of * type that this deserializer handles. */ protected JsonDeserializer<Object> _findSubclassDeserializer(DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens) throws IOException, JsonProcessingException { JsonDeserializer<Object> 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<ClassKey,JsonDeserializer<Object>>();; } _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. *<p> * Rules for wrapping and unwrapping are bit complicated; essentially: *<ul> * <li>Errors are to be passed as is (if uncovered via unwrapping) * <li>"Plain" IOExceptions (ones that are not of type * {@link JsonMappingException} are to be passed as is *</ul> */ public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) throws IOException { /* 05-Mar-2009, tatu: But one nasty edge is when we get * StackOverflow: usually due to infinite loop. But that * usually gets hidden within an InvocationTargetException... */ while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors and "plain" IOExceptions to be passed as is if (t instanceof Error) { throw (Error) t; } boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); // Ditto for IOExceptions; except we may want to wrap mapping exceptions if (t instanceof IOException) { if (!wrap || !(t instanceof JsonMappingException)) { throw (IOException) t; } } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions if (t instanceof RuntimeException) { throw (RuntimeException) t; } } // [JACKSON-55] Need to add reference information throw JsonMappingException.wrapWithPath(t, bean, fieldName); } public void wrapAndThrow(Throwable t, Object bean, int index, DeserializationContext ctxt) throws IOException { while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors and "plain" IOExceptions to be passed as is if (t instanceof Error) { throw (Error) t; } boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); // Ditto for IOExceptions; except we may want to wrap mapping exceptions if (t instanceof IOException) { if (!wrap || !(t instanceof JsonMappingException)) { throw (IOException) t; } } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions if (t instanceof RuntimeException) { throw (RuntimeException) t; } } // [JACKSON-55] Need to add reference information throw JsonMappingException.wrapWithPath(t, bean, index); } protected void 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 if (t instanceof Error) { throw (Error) t; } boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); if (t instanceof IOException) { // Since we have no more information to add, let's not actually wrap.. throw (IOException) t; } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions if (t instanceof RuntimeException) { throw (RuntimeException) t; } } throw ctxt.instantiationException(_beanType.getRawClass(), t); } }