| package org.codehaus.jackson.map.deser; |
| |
| import java.io.IOException; |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| import org.codehaus.jackson.*; |
| import org.codehaus.jackson.annotate.JsonTypeInfo; |
| import org.codehaus.jackson.map.*; |
| import org.codehaus.jackson.map.annotate.JsonCachable; |
| import org.codehaus.jackson.map.deser.impl.*; |
| import org.codehaus.jackson.map.deser.std.ContainerDeserializerBase; |
| import org.codehaus.jackson.map.deser.std.StdDeserializer; |
| import org.codehaus.jackson.map.introspect.AnnotatedClass; |
| import org.codehaus.jackson.map.introspect.AnnotatedMember; |
| import org.codehaus.jackson.map.introspect.AnnotatedWithParams; |
| import org.codehaus.jackson.map.type.ClassKey; |
| import org.codehaus.jackson.map.util.ClassUtil; |
| import org.codehaus.jackson.type.JavaType; |
| import org.codehaus.jackson.util.TokenBuffer; |
| |
| /** |
| * Deserializer class that can deserialize instances of |
| * arbitrary bean objects, usually from JSON Object structs, |
| * but possibly also from simple types like String values. |
| */ |
| @JsonCachable |
| /* Because of costs associated with constructing bean deserializers, |
| * they usually should be cached unlike other deserializer types. |
| * But more importantly, it is important to be able to cache |
| * bean serializers to handle cyclic references. |
| */ |
| public class BeanDeserializer |
| extends StdDeserializer<Object> |
| implements ResolvableDeserializer |
| { |
| /* |
| /********************************************************** |
| /* Information regarding type being deserialized |
| /********************************************************** |
| */ |
| |
| /** |
| * Class for which deserializer is built; used for accessing |
| * annotations during resolution phase (see {@link #resolve}). |
| */ |
| final protected AnnotatedClass _forClass; |
| |
| /** |
| * Declared type of the bean this deserializer handles. |
| */ |
| final protected JavaType _beanType; |
| |
| /** |
| * Property that contains value to be deserialized using |
| * deserializer; mostly needed to find contextual annotations |
| * for subtypes. |
| * |
| * @since 1.7 |
| */ |
| final protected BeanProperty _property; |
| |
| /* |
| /********************************************************** |
| /* Construction configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * 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. |
| */ |
| protected final 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; |
| |
| /* |
| /********************************************************** |
| /* Property information, setters |
| /********************************************************** |
| */ |
| |
| /** |
| * Mapping of property names to properties, built when all properties |
| * to use have been successfully resolved. |
| * |
| * @since 1.7 |
| */ |
| 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. |
| * |
| * @since 1.9 |
| */ |
| 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; |
| |
| /** |
| * 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. |
| */ |
| protected HashMap<ClassKey, JsonDeserializer<Object>> _subDeserializers; |
| |
| /** |
| * If one of properties has "unwrapped" value, we need separate |
| * helper object |
| * |
| * @since 1.9 |
| */ |
| protected UnwrappedPropertyHandler _unwrappedPropertyHandler; |
| |
| /** |
| * Handler that we need iff any of properties uses external |
| * type id. |
| */ |
| protected ExternalTypeHandler _externalTypeIdHandler; |
| |
| /* |
| /********************************************************** |
| /* Life-cycle, construction, initialization |
| /********************************************************** |
| */ |
| |
| /** |
| * @deprecated (since 1.9) Use the constructor that takes {@link ValueInstantiator} instead |
| */ |
| @Deprecated |
| public BeanDeserializer(AnnotatedClass forClass, JavaType type, BeanProperty property, |
| CreatorCollector creators, |
| BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs, |
| HashSet<String> ignorableProps, boolean ignoreAllUnknown, |
| SettableAnyProperty anySetter) |
| { |
| this(forClass, type, property, |
| creators.constructValueInstantiator(null), |
| properties, backRefs, |
| ignorableProps, ignoreAllUnknown, |
| anySetter, null); |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| public BeanDeserializer(BeanDescription beanDesc, BeanProperty property, |
| ValueInstantiator valueInstantiator, |
| BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs, |
| HashSet<String> ignorableProps, boolean ignoreAllUnknown, |
| SettableAnyProperty anySetter, List<ValueInjector> injectables) |
| { |
| this(beanDesc.getClassInfo(), beanDesc.getType(), property, |
| valueInstantiator, |
| properties, backRefs, |
| ignorableProps, ignoreAllUnknown, |
| anySetter, injectables); |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| protected BeanDeserializer(AnnotatedClass forClass, JavaType type, BeanProperty property, |
| ValueInstantiator valueInstantiator, |
| BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs, |
| HashSet<String> ignorableProps, boolean ignoreAllUnknown, |
| SettableAnyProperty anySetter, List<ValueInjector> injectables) |
| { |
| super(type); |
| _forClass = forClass; |
| _beanType = type; |
| _property = property; |
| |
| _valueInstantiator = valueInstantiator; |
| if (valueInstantiator.canCreateFromObjectWith()) { |
| _propertyBasedCreator = new PropertyBasedCreator(valueInstantiator); |
| } else { |
| _propertyBasedCreator = null; |
| } |
| |
| _beanProperties = properties; |
| _backRefs = backRefs; |
| _ignorableProps = ignorableProps; |
| _ignoreAllUnknown = ignoreAllUnknown; |
| _anySetter = anySetter; |
| _injectables = (injectables == null || injectables.isEmpty()) ? null |
| : injectables.toArray(new ValueInjector[injectables.size()]); |
| |
| _nonStandardCreation = valueInstantiator.canCreateUsingDelegate() |
| || (_propertyBasedCreator != null) |
| || !valueInstantiator.canCreateUsingDefault() |
| || (_unwrappedPropertyHandler != null); |
| } |
| |
| /** |
| * Copy-constructor that can be used by sub-classes to allow |
| * copy-on-write styling copying of settings of an existing instance. |
| * |
| * @since 1.7 |
| */ |
| protected BeanDeserializer(BeanDeserializer src) |
| { |
| this(src, src._ignoreAllUnknown); |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| protected BeanDeserializer(BeanDeserializer src, boolean ignoreAllUnknown) |
| { |
| super(src._beanType); |
| |
| _forClass = src._forClass; |
| _beanType = src._beanType; |
| _property = src._property; |
| |
| _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; |
| |
| _nonStandardCreation = src._nonStandardCreation; |
| _unwrappedPropertyHandler = src._unwrappedPropertyHandler; |
| } |
| |
| @Override |
| public JsonDeserializer<Object> unwrappingDeserializer() |
| { |
| /* bit kludgy but we don't want to accidentally change type; |
| * sub-classes MUST override this method to support unwrapped |
| * properties... |
| */ |
| if (getClass() != BeanDeserializer.class) { |
| return this; |
| } |
| /* main thing really is to just enforce ignoring of unknown |
| * properties; since there may be multiple unwrapped values |
| * and properties for all may be interleaved... |
| */ |
| return new BeanDeserializer(this, true); |
| } |
| |
| /* |
| /********************************************************** |
| /* Public accessors |
| /********************************************************** |
| */ |
| |
| public boolean hasProperty(String propertyName) { |
| return _beanProperties.find(propertyName) != null; |
| } |
| |
| /** |
| * Accessor for checking number of deserialized properties. |
| * |
| * @since 1.7 |
| */ |
| public int getPropertyCount() { |
| return _beanProperties.size(); |
| } |
| |
| public final Class<?> getBeanClass() { return _beanType.getRawClass(); } |
| |
| @Override public JavaType getValueType() { return _beanType; } |
| |
| /** |
| * |
| * @since 1.6 |
| */ |
| public Iterator<SettableBeanProperty> properties() |
| { |
| if (_beanProperties == null) { // since 1.7 |
| throw new IllegalStateException("Can only call before BeanDeserializer has been resolved"); |
| } |
| return _beanProperties.allProperties(); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| public ValueInstantiator getValueInstantiator() { |
| return _valueInstantiator; |
| } |
| |
| /* |
| /********************************************************** |
| /* 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(DeserializationConfig config, DeserializerProvider provider) |
| throws JsonMappingException |
| { |
| Iterator<SettableBeanProperty> it = _beanProperties.allProperties(); |
| UnwrappedPropertyHandler unwrapped = null; |
| ExternalTypeHandler.Builder extTypes = null; |
| |
| while (it.hasNext()) { |
| SettableBeanProperty origProp = it.next(); |
| SettableBeanProperty prop = origProp; |
| // May already have deserializer from annotations, if so, skip: |
| if (!prop.hasValueDeserializer()) { |
| prop = prop.withValueDeserializer(findDeserializer(config, provider, prop.getType(), prop)); |
| } |
| // [JACKSON-235]: need to link managed references with matching back references |
| prop = _resolveManagedReferenceProperty(config, prop); |
| // [JACKSON-132]: support unwrapped values (via @JsonUnwrapped) |
| SettableBeanProperty u = _resolveUnwrappedProperty(config, prop); |
| if (u != null) { |
| prop = u; |
| if (unwrapped == null) { |
| unwrapped = new UnwrappedPropertyHandler(); |
| } |
| unwrapped.addProperty(prop); |
| } |
| // [JACKSON-594]: non-static inner classes too: |
| prop = _resolveInnerClassValuedProperty(config, 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); |
| } |
| } |
| } |
| |
| // Finally, "any setter" may also need to be resolved now |
| if (_anySetter != null && !_anySetter.hasValueDeserializer()) { |
| _anySetter = _anySetter.withValueDeserializer(findDeserializer(config, provider, _anySetter.getType(), _anySetter.getProperty())); |
| } |
| |
| // as well as delegate-based constructor: |
| if (_valueInstantiator.canCreateUsingDelegate()) { |
| JavaType delegateType = _valueInstantiator.getDelegateType(); |
| 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, _forClass.getAnnotations(), delegateCreator); |
| _delegateDeserializer = findDeserializer(config, provider, delegateType, property); |
| } |
| // or property-based one |
| // IMPORTANT: must access properties that _propertyBasedCreator has |
| if (_propertyBasedCreator != null) { |
| for (SettableBeanProperty prop : _propertyBasedCreator.getCreatorProperties()) { |
| if (!prop.hasValueDeserializer()) { |
| _propertyBasedCreator.assignDeserializer(prop, |
| findDeserializer(config, provider, prop.getType(), prop)); |
| } |
| } |
| } |
| 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; |
| } |
| } |
| |
| /** |
| * Helper method called to see if given property is part of 'managed' property |
| * pair (managed + back reference), and if so, handle resolution details. |
| * |
| * @since 1.9 |
| */ |
| protected SettableBeanProperty _resolveManagedReferenceProperty(DeserializationConfig config, |
| SettableBeanProperty prop) |
| { |
| String refName = prop.getManagedReferenceName(); |
| if (refName == null) { |
| return prop; |
| } |
| JsonDeserializer<?> valueDeser = prop.getValueDeserializer(); |
| SettableBeanProperty backProp = null; |
| boolean isContainer = false; |
| if (valueDeser instanceof BeanDeserializer) { |
| backProp = ((BeanDeserializer) valueDeser).findBackReference(refName); |
| } else if (valueDeser instanceof ContainerDeserializerBase<?>) { |
| JsonDeserializer<?> contentDeser = ((ContainerDeserializerBase<?>) valueDeser).getContentDeserializer(); |
| if (!(contentDeser instanceof BeanDeserializer)) { |
| 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 "+contentDeser.getClass().getName()+")"); |
| } |
| backProp = ((BeanDeserializer) contentDeser).findBackReference(refName); |
| isContainer = true; |
| } else if (valueDeser instanceof AbstractDeserializer) { // [JACKSON-368]: not easy to fix, alas |
| throw new IllegalArgumentException("Can not handle managed/back reference for abstract types (property "+_beanType.getRawClass().getName()+"."+prop.getName()+")"); |
| } 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 SettableBeanProperty.ManagedReferenceProperty(refName, prop, backProp, |
| _forClass.getAnnotations(), isContainer); |
| } |
| |
| /** |
| * Helper method called to see if given property might be so-called unwrapped |
| * property: these require special handling. |
| * |
| * @since 1.9 |
| */ |
| protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationConfig config, |
| SettableBeanProperty prop) |
| { |
| AnnotatedMember am = prop.getMember(); |
| if (am != null && config.getAnnotationIntrospector().shouldUnwrapProperty(am) == Boolean.TRUE) { |
| JsonDeserializer<Object> orig = prop.getValueDeserializer(); |
| JsonDeserializer<Object> unwrapping = orig.unwrappingDeserializer(); |
| 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... |
| * |
| * @since 1.9 |
| */ |
| protected SettableBeanProperty _resolveInnerClassValuedProperty(DeserializationConfig config, |
| 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 BeanDeserializer; but for now it'll have to do |
| if (deser instanceof BeanDeserializer) { |
| BeanDeserializer bd = (BeanDeserializer) 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 (config.isEnabled(DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS)) { |
| ClassUtil.checkAndFixAccess(ctor); |
| } |
| return new SettableBeanProperty.InnerClassProperty(prop, ctor); |
| } |
| } |
| } |
| } |
| } |
| return prop; |
| } |
| |
| /* |
| /********************************************************** |
| /* JsonDeserializer implementation |
| /********************************************************** |
| */ |
| |
| /** |
| * Main deserialization method for bean-based objects (POJOs). |
| */ |
| @Override |
| public final Object deserialize(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| JsonToken t = jp.getCurrentToken(); |
| // common case first: |
| if (t == JsonToken.START_OBJECT) { |
| jp.nextToken(); |
| return deserializeFromObject(jp, ctxt); |
| } |
| // and then others, generally requiring use of @JsonCreator |
| switch (t) { |
| case VALUE_STRING: |
| return deserializeFromString(jp, ctxt); |
| case VALUE_NUMBER_INT: |
| return deserializeFromNumber(jp, ctxt); |
| case VALUE_NUMBER_FLOAT: |
| return deserializeFromDouble(jp, ctxt); |
| case VALUE_EMBEDDED_OBJECT: |
| return jp.getEmbeddedObject(); |
| case VALUE_TRUE: |
| case VALUE_FALSE: |
| return deserializeFromBoolean(jp, ctxt); |
| case START_ARRAY: |
| // these only work if there's a (delegating) creator... |
| return deserializeFromArray(jp, ctxt); |
| case FIELD_NAME: |
| case END_OBJECT: // added to resolve [JACKSON-319], possible related issues |
| return deserializeFromObject(jp, ctxt); |
| } |
| throw ctxt.mappingException(getBeanClass()); |
| } |
| |
| /** |
| * Secondary deserialization method, called in cases where POJO |
| * instance is created as part of deserialization, potentially |
| * after collecting some or all of the properties to set. |
| */ |
| @Override |
| public Object deserialize(JsonParser jp, DeserializationContext ctxt, Object bean) |
| throws IOException, JsonProcessingException |
| { |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| if (_unwrappedPropertyHandler != null) { |
| return deserializeWithUnwrapped(jp, ctxt, bean); |
| } |
| if (_externalTypeIdHandler != null) { |
| return deserializeWithExternalTypeId(jp, ctxt, bean); |
| } |
| JsonToken t = jp.getCurrentToken(); |
| // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... |
| if (t == JsonToken.START_OBJECT) { |
| t = jp.nextToken(); |
| } |
| for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| // Skip field name: |
| jp.nextToken(); |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| |
| if (prop != null) { // normal case |
| try { |
| prop.deserializeAndSet(jp, ctxt, bean); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| /* As per [JACKSON-313], things marked as ignorable should not be |
| * passed to any setter |
| */ |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| } else if (_anySetter != null) { |
| _anySetter.deserializeAndSet(jp, ctxt, bean, propName); |
| continue; |
| } else { |
| // Unknown: let's call handler method |
| handleUnknownProperty(jp, ctxt, bean, propName); |
| } |
| } |
| return bean; |
| } |
| |
| @Override |
| public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, |
| TypeDeserializer typeDeserializer) |
| throws IOException, JsonProcessingException |
| { |
| // In future could check current token... for now this should be enough: |
| return typeDeserializer.deserializeTypedFromObject(jp, ctxt); |
| } |
| |
| /* |
| /********************************************************** |
| /* Concrete deserialization methods |
| /********************************************************** |
| */ |
| |
| public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_nonStandardCreation) { |
| if (_unwrappedPropertyHandler != null) { |
| return deserializeWithUnwrapped(jp, ctxt); |
| } |
| if (_externalTypeIdHandler != null) { |
| return deserializeWithExternalTypeId(jp, ctxt); |
| } |
| return deserializeFromObjectUsingNonDefault(jp, ctxt); |
| } |
| |
| final Object bean = _valueInstantiator.createUsingDefault(); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| // Skip field name: |
| jp.nextToken(); |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { // normal case |
| try { |
| prop.deserializeAndSet(jp, ctxt, bean); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| _handleUnknown(jp, ctxt, bean, propName); |
| } |
| return bean; |
| } |
| |
| private final void _handleUnknown(JsonParser jp, DeserializationContext ctxt, |
| Object bean, String propName) |
| throws IOException, JsonProcessingException |
| { |
| /* As per [JACKSON-313], things marked as ignorable should not be |
| * passed to any setter |
| */ |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| } else if (_anySetter != null) { |
| try { |
| _anySetter.deserializeAndSet(jp, ctxt, bean, propName); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| } else { |
| // Unknown: let's call handler method |
| handleUnknownProperty(jp, ctxt, bean, propName); |
| } |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| protected Object deserializeFromObjectUsingNonDefault(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_delegateDeserializer != null) { |
| return _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| } |
| if (_propertyBasedCreator != null) { |
| return _deserializeUsingPropertyBased(jp, ctxt); |
| } |
| // should only occur for abstract types... |
| if (_beanType.isAbstract()) { |
| throw JsonMappingException.from(jp, "Can not instantiate abstract type "+_beanType |
| +" (need to add/enable type information?)"); |
| } |
| throw JsonMappingException.from(jp, "No suitable constructor found for type " |
| +_beanType+": can not instantiate from JSON object (need to add/enable type information?)"); |
| } |
| |
| public Object deserializeFromString(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| /* Bit complicated if we have delegating creator; may need to use it, |
| * or might not... |
| */ |
| if (_delegateDeserializer != null) { |
| if (!_valueInstantiator.canCreateFromString()) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| } |
| return _valueInstantiator.createFromString(jp.getText()); |
| } |
| |
| public Object deserializeFromNumber(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| switch (jp.getNumberType()) { |
| case INT: |
| if (_delegateDeserializer != null) { |
| if (!_valueInstantiator.canCreateFromInt()) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| } |
| return _valueInstantiator.createFromInt(jp.getIntValue()); |
| case LONG: |
| if (_delegateDeserializer != null) { |
| if (!_valueInstantiator.canCreateFromInt()) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| } |
| return _valueInstantiator.createFromLong(jp.getLongValue()); |
| } |
| // actually, could also be BigInteger, so: |
| if (_delegateDeserializer != null) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| throw ctxt.instantiationException(getBeanClass(), "no suitable creator method found to deserialize from JSON integer number"); |
| } |
| |
| /** |
| * Method called to deserialize POJO value from a JSON floating-point |
| * number. |
| * |
| * @since 1.9 |
| */ |
| public Object deserializeFromDouble(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| switch (jp.getNumberType()) { |
| case FLOAT: // no separate methods for taking float... |
| case DOUBLE: |
| if (_delegateDeserializer != null) { |
| if (!_valueInstantiator.canCreateFromDouble()) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| } |
| return _valueInstantiator.createFromDouble(jp.getDoubleValue()); |
| } |
| // actually, could also be BigDecimal, so: |
| if (_delegateDeserializer != null) { |
| return _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| } |
| throw ctxt.instantiationException(getBeanClass(), "no suitable creator method found to deserialize from JSON floating-point number"); |
| } |
| |
| /** |
| * Method called to deserialize POJO value from a JSON boolean |
| * value (true, false) |
| * |
| * @since 1.9 |
| */ |
| public Object deserializeFromBoolean(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_delegateDeserializer != null) { |
| if (!_valueInstantiator.canCreateFromBoolean()) { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } |
| } |
| boolean value = (jp.getCurrentToken() == JsonToken.VALUE_TRUE); |
| return _valueInstantiator.createFromBoolean(value); |
| } |
| |
| /** |
| * @since 1.9 |
| */ |
| public Object deserializeFromArray(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_delegateDeserializer != null) { |
| try { |
| Object bean = _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| return bean; |
| } catch (Exception e) { |
| wrapInstantiationProblem(e, ctxt); |
| } |
| } |
| throw ctxt.mappingException(getBeanClass()); |
| } |
| |
| /** |
| * Method called to deserialize bean using "property-based creator": |
| * this means that a non-default constructor or factory method is |
| * called, and then possibly other setters. The trick is that |
| * values for creator method need to be buffered, first; and |
| * due to non-guaranteed ordering possibly some other properties |
| * as well. |
| * |
| * @since 1.2 |
| */ |
| protected final Object _deserializeUsingPropertyBased(final JsonParser jp, final DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| final PropertyBasedCreator creator = _propertyBasedCreator; |
| PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt); |
| |
| // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases |
| TokenBuffer unknown = null; |
| |
| JsonToken t = jp.getCurrentToken(); |
| for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| jp.nextToken(); // to point to value |
| // creator property? |
| SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); |
| if (creatorProp != null) { |
| // Last creator property to set? |
| Object value = creatorProp.deserialize(jp, ctxt); |
| if (buffer.assignParameter(creatorProp.getPropertyIndex(), value)) { |
| jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); |
| continue; // never gets here |
| } |
| // polymorphic? |
| if (bean.getClass() != _beanType.getRawClass()) { |
| return handlePolymorphic(jp, ctxt, bean, unknown); |
| } |
| if (unknown != null) { // nope, just extra unknown stuff... |
| bean = handleUnknownProperties(ctxt, bean, unknown); |
| } |
| // or just clean? |
| return deserialize(jp, ctxt, bean); |
| } |
| continue; |
| } |
| // regular property? needs buffering |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { |
| buffer.bufferProperty(prop, prop.deserialize(jp, ctxt)); |
| continue; |
| } |
| /* As per [JACKSON-313], things marked as ignorable should not be |
| * passed to any setter |
| */ |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| // "any property"? |
| if (_anySetter != null) { |
| buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt)); |
| continue; |
| } |
| // Ok then, let's collect the whole field; name and value |
| if (unknown == null) { |
| unknown = new TokenBuffer(jp.getCodec()); |
| } |
| unknown.writeFieldName(propName); |
| unknown.copyCurrentStructure(jp); |
| } |
| |
| // We hit END_OBJECT, so: |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapInstantiationProblem(e, ctxt); |
| return null; // never gets here |
| } |
| if (unknown != null) { |
| // polymorphic? |
| if (bean.getClass() != _beanType.getRawClass()) { |
| return handlePolymorphic(null, ctxt, bean, unknown); |
| } |
| // no, just some extra unknown properties |
| return handleUnknownProperties(ctxt, bean, unknown); |
| } |
| return bean; |
| } |
| |
| /** |
| * 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 jp (optional) If not null, parser that has more properties to handle |
| * (in addition to buffered properties); if null, all properties are passed |
| * in buffer |
| */ |
| protected Object handlePolymorphic(JsonParser jp, DeserializationContext ctxt, |
| Object bean, TokenBuffer unknownTokens) |
| throws IOException, JsonProcessingException |
| { |
| // First things first: maybe there is a more specific deserializer available? |
| JsonDeserializer<Object> 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 (jp != null) { |
| bean = subDeser.deserialize(jp, 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 (jp != null) { |
| bean = deserialize(jp, ctxt, bean); |
| } |
| return bean; |
| } |
| |
| /* |
| /********************************************************** |
| /* Handling for cases where we have "unwrapped" values |
| /********************************************************** |
| */ |
| |
| /** |
| * Method called when there are declared "unwrapped" properties |
| * which need special handling |
| */ |
| protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_delegateDeserializer != null) { |
| return _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); |
| } |
| if (_propertyBasedCreator != null) { |
| return deserializeUsingPropertyBasedWithUnwrapped(jp, ctxt); |
| } |
| |
| TokenBuffer tokens = new TokenBuffer(jp.getCodec()); |
| tokens.writeStartObject(); |
| final Object bean = _valueInstantiator.createUsingDefault(); |
| |
| if (_injectables != null) { |
| injectValues(ctxt, bean); |
| } |
| |
| for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| jp.nextToken(); |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { // normal case |
| try { |
| prop.deserializeAndSet(jp, ctxt, bean); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| // ignorable things should be ignored |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| // but... others should be passed to unwrapped property deserializers |
| tokens.writeFieldName(propName); |
| tokens.copyCurrentStructure(jp); |
| // how about any setter? We'll get copies but... |
| if (_anySetter != null) { |
| try { |
| _anySetter.deserializeAndSet(jp, ctxt, bean, propName); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| } |
| tokens.writeEndObject(); |
| _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); |
| return bean; |
| } |
| |
| protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt, Object bean) |
| throws IOException, JsonProcessingException |
| { |
| JsonToken t = jp.getCurrentToken(); |
| if (t == JsonToken.START_OBJECT) { |
| t = jp.nextToken(); |
| } |
| TokenBuffer tokens = new TokenBuffer(jp.getCodec()); |
| tokens.writeStartObject(); |
| for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| jp.nextToken(); |
| if (prop != null) { // normal case |
| try { |
| prop.deserializeAndSet(jp, ctxt, bean); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| // but... others should be passed to unwrapped property deserializers |
| tokens.writeFieldName(propName); |
| tokens.copyCurrentStructure(jp); |
| // how about any setter? We'll get copies but... |
| if (_anySetter != null) { |
| _anySetter.deserializeAndSet(jp, ctxt, bean, propName); |
| } |
| } |
| tokens.writeEndObject(); |
| _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); |
| return bean; |
| } |
| |
| protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| final PropertyBasedCreator creator = _propertyBasedCreator; |
| PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt); |
| |
| TokenBuffer tokens = new TokenBuffer(jp.getCodec()); |
| tokens.writeStartObject(); |
| |
| JsonToken t = jp.getCurrentToken(); |
| for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| jp.nextToken(); // to point to value |
| // creator property? |
| SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); |
| if (creatorProp != null) { |
| // Last creator property to set? |
| Object value = creatorProp.deserialize(jp, ctxt); |
| if (buffer.assignParameter(creatorProp.getPropertyIndex(), value)) { |
| t = jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); |
| continue; // never gets here |
| } |
| // if so, need to copy all remaining tokens into buffer |
| while (t == JsonToken.FIELD_NAME) { |
| jp.nextToken(); // to skip name |
| tokens.copyCurrentStructure(jp); |
| t = jp.nextToken(); |
| } |
| tokens.writeEndObject(); |
| if (bean.getClass() != _beanType.getRawClass()) { |
| // !!! 08-Jul-2011, tatu: Could probably support; but for now |
| // it's too complicated, so bail out |
| throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values"); |
| } |
| return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); |
| } |
| continue; |
| } |
| // regular property? needs buffering |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { |
| buffer.bufferProperty(prop, prop.deserialize(jp, ctxt)); |
| continue; |
| } |
| /* As per [JACKSON-313], things marked as ignorable should not be |
| * passed to any setter |
| */ |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| tokens.writeFieldName(propName); |
| tokens.copyCurrentStructure(jp); |
| // "any property"? |
| if (_anySetter != null) { |
| buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt)); |
| } |
| } |
| |
| // We hit END_OBJECT, so: |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapInstantiationProblem(e, ctxt); |
| return null; // never gets here |
| } |
| return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); |
| } |
| |
| /* |
| /********************************************************** |
| /* Handling for cases where we have property/-ies wth |
| /* external type id |
| /********************************************************** |
| */ |
| |
| protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| if (_propertyBasedCreator != null) { |
| return deserializeUsingPropertyBasedWithExternalTypeId(jp, ctxt); |
| } |
| return deserializeWithExternalTypeId(jp, ctxt, _valueInstantiator.createUsingDefault()); |
| } |
| |
| protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt, |
| Object bean) |
| throws IOException, JsonProcessingException |
| { |
| final ExternalTypeHandler ext = _externalTypeIdHandler.start(); |
| for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| jp.nextToken(); |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { // normal case |
| // [JACKSON-831]: may have property AND be used as external type id: |
| if (jp.getCurrentToken().isScalarValue()) { |
| ext.handleTypePropertyValue(jp, ctxt, propName, bean); |
| } |
| try { |
| prop.deserializeAndSet(jp, ctxt, bean); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } |
| // ignorable things should be ignored |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| // but others are likely to be part of external type id thingy... |
| if (ext.handleToken(jp, ctxt, propName, bean)) { |
| continue; |
| } |
| // if not, the usual fallback handling: |
| if (_anySetter != null) { |
| try { |
| _anySetter.deserializeAndSet(jp, ctxt, bean, propName); |
| } catch (Exception e) { |
| wrapAndThrow(e, bean, propName, ctxt); |
| } |
| continue; |
| } else { |
| // Unknown: let's call handler method |
| handleUnknownProperty(jp, ctxt, bean, propName); |
| } |
| } |
| // and when we get this far, let's try finalizing the deal: |
| return ext.complete(jp, ctxt, bean); |
| } |
| |
| protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| final ExternalTypeHandler ext = _externalTypeIdHandler.start(); |
| final PropertyBasedCreator creator = _propertyBasedCreator; |
| PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt); |
| |
| TokenBuffer tokens = new TokenBuffer(jp.getCodec()); |
| tokens.writeStartObject(); |
| |
| JsonToken t = jp.getCurrentToken(); |
| for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { |
| String propName = jp.getCurrentName(); |
| jp.nextToken(); // to point to value |
| // creator property? |
| SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); |
| if (creatorProp != null) { |
| // Last creator property to set? |
| Object value = creatorProp.deserialize(jp, ctxt); |
| if (buffer.assignParameter(creatorProp.getPropertyIndex(), value)) { |
| t = jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); |
| continue; // never gets here |
| } |
| // if so, need to copy all remaining tokens into buffer |
| while (t == JsonToken.FIELD_NAME) { |
| jp.nextToken(); // to skip name |
| tokens.copyCurrentStructure(jp); |
| t = jp.nextToken(); |
| } |
| if (bean.getClass() != _beanType.getRawClass()) { |
| // !!! 08-Jul-2011, tatu: Could probably support; but for now |
| // it's too complicated, so bail out |
| throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values"); |
| } |
| return ext.complete(jp, ctxt, bean); |
| } |
| continue; |
| } |
| // regular property? needs buffering |
| SettableBeanProperty prop = _beanProperties.find(propName); |
| if (prop != null) { |
| buffer.bufferProperty(prop, prop.deserialize(jp, ctxt)); |
| continue; |
| } |
| // external type id (or property that depends on it)? |
| if (ext.handleToken(jp, ctxt, propName, null)) { |
| continue; |
| } |
| /* As per [JACKSON-313], things marked as ignorable should not be |
| * passed to any setter |
| */ |
| if (_ignorableProps != null && _ignorableProps.contains(propName)) { |
| jp.skipChildren(); |
| continue; |
| } |
| // "any property"? |
| if (_anySetter != null) { |
| buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt)); |
| } |
| } |
| |
| // We hit END_OBJECT, so: |
| Object bean; |
| try { |
| bean = creator.build(buffer); |
| } catch (Exception e) { |
| wrapInstantiationProblem(e, ctxt); |
| return null; // never gets here |
| } |
| return ext.complete(jp, ctxt, bean); |
| } |
| |
| /* |
| /********************************************************** |
| /* 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 |
| DeserializerProvider deserProv = ctxt.getDeserializerProvider(); |
| if (deserProv != null) { |
| JavaType type = ctxt.constructType(bean.getClass()); |
| /* 09-Dec-2010, tatu: Would be nice to know which property pointed to this |
| * bean... but, alas, no such information is retained, so: |
| */ |
| subDeser = deserProv.findValueDeserializer(ctxt.getConfig(), type, _property); |
| // 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(DeserializationConfig.Feature.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(DeserializationConfig.Feature.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(DeserializationConfig.Feature.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); |
| } |
| |
| /** |
| * @deprecated Since 1.7 use variant that takes {@link DeserializationContext} |
| */ |
| @Deprecated |
| public void wrapAndThrow(Throwable t, Object bean, String fieldName) |
| throws IOException |
| { |
| wrapAndThrow(t, bean, fieldName, null); |
| } |
| |
| /** |
| * @deprecated Since 1.7 use variant that takes {@link DeserializationContext} |
| */ |
| @Deprecated |
| public void wrapAndThrow(Throwable t, Object bean, int index) |
| throws IOException |
| { |
| wrapAndThrow(t, bean, index, null); |
| } |
| } |