| package com.fasterxml.jackson.databind.deser; |
| |
| import java.io.IOException; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| import com.fasterxml.jackson.core.*; |
| import com.fasterxml.jackson.core.util.InternCache; |
| import com.fasterxml.jackson.databind.*; |
| import com.fasterxml.jackson.databind.introspect.AnnotatedField; |
| import com.fasterxml.jackson.databind.introspect.AnnotatedMember; |
| import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; |
| import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; |
| import com.fasterxml.jackson.databind.util.Annotations; |
| import com.fasterxml.jackson.databind.util.ClassUtil; |
| import com.fasterxml.jackson.databind.util.ViewMatcher; |
| |
| /** |
| * Base class for settable properties of a bean: contains |
| * both type and name definitions, and reflection-based set functionality. |
| * Concrete sub-classes implement details, so that both field- and |
| * setter-backed properties can be handled |
| */ |
| public abstract class SettableBeanProperty |
| implements BeanProperty |
| { |
| /** |
| * Logical name of the property (often but not always derived |
| * from the setter method name) |
| */ |
| protected final String _propName; |
| |
| /** |
| * Base type for property; may be a supertype of actual value. |
| */ |
| protected final JavaType _type; |
| |
| /** |
| * Class that contains this property (either class that declares |
| * the property or one of its subclasses), class that is |
| * deserialized using deserializer that contains this property. |
| */ |
| protected final Annotations _contextAnnotations; |
| |
| /** |
| * Deserializer used for handling property value. |
| */ |
| protected JsonDeserializer<Object> _valueDeserializer; |
| |
| /** |
| * If value will contain type information (to support |
| * polymorphic handling), this is the type deserializer |
| * used to handle type resolution. |
| */ |
| protected TypeDeserializer _valueTypeDeserializer; |
| |
| /** |
| * Object used to figure out value to be used when 'null' literal is encountered in JSON. |
| * For most types simply Java null, but for primitive types must |
| * be a non-null value (like Integer.valueOf(0) for int). |
| */ |
| protected NullProvider _nullProvider; |
| |
| /** |
| * If property represents a managed (forward) reference |
| * (see [JACKSON-235]), we will need name of reference for |
| * later linking. |
| */ |
| protected String _managedReferenceName; |
| |
| /** |
| * Helper object used for checking whether this property is to |
| * be included in the active view, if property is view-specific; |
| * null otherwise. |
| */ |
| protected ViewMatcher _viewMatcher; |
| |
| /** |
| * Index of property (within all property of a bean); assigned |
| * when all properties have been collected. Order of entries |
| * is arbitrary, but once indexes are assigned they are not |
| * changed. |
| */ |
| protected int _propertyIndex = -1; |
| |
| /* |
| /********************************************************** |
| /* Life-cycle (construct & configure) |
| /********************************************************** |
| */ |
| |
| protected SettableBeanProperty(String propName, JavaType type, TypeDeserializer typeDeser, |
| Annotations contextAnnotations) |
| { |
| /* 09-Jan-2009, tatu: Intern()ing makes sense since Jackson parsed |
| * field names are (usually) interned too, hence lookups will be faster. |
| */ |
| // 23-Oct-2009, tatu: should this be disabled wrt [JACKSON-180]? |
| /* Probably need not, given that namespace of field/method names |
| * is not unbounded, unlike potential JSON names. |
| */ |
| if (propName == null || propName.length() == 0) { |
| _propName = ""; |
| } else { |
| _propName = InternCache.instance.intern(propName); |
| } |
| _type = type; |
| _contextAnnotations = contextAnnotations; |
| _viewMatcher = null; |
| |
| // 30-Jan-2012, tatu: Important: contextualize TypeDeserializer now... |
| if (typeDeser != null) { |
| typeDeser = typeDeser.forProperty(this); |
| } |
| _valueTypeDeserializer = typeDeser; |
| } |
| |
| /** |
| * Basic copy-constructor for sub-classes to use. |
| */ |
| protected SettableBeanProperty(SettableBeanProperty src) |
| { |
| _propName = src._propName; |
| _type = src._type; |
| _contextAnnotations = src._contextAnnotations; |
| _valueDeserializer = src._valueDeserializer; |
| _valueTypeDeserializer = src._valueTypeDeserializer; |
| _nullProvider = src._nullProvider; |
| _managedReferenceName = src._managedReferenceName; |
| _propertyIndex = src._propertyIndex; |
| _viewMatcher = src._viewMatcher; |
| } |
| |
| /** |
| * Copy-with-deserializer-change constructor for sub-classes to use. |
| */ |
| protected SettableBeanProperty(SettableBeanProperty src, JsonDeserializer<Object> deser) |
| { |
| _propName = src._propName; |
| _type = src._type; |
| _contextAnnotations = src._contextAnnotations; |
| _valueTypeDeserializer = src._valueTypeDeserializer; |
| _managedReferenceName = src._managedReferenceName; |
| _propertyIndex = src._propertyIndex; |
| |
| _valueDeserializer = deser; |
| if (deser == null) { |
| _nullProvider = null; |
| } else { |
| Object nvl = deser.getNullValue(); |
| _nullProvider = (nvl == null) ? null : new NullProvider(_type, nvl); |
| } |
| _viewMatcher = src._viewMatcher; |
| } |
| |
| /** |
| * Copy-with-deserializer-change constructor for sub-classes to use. |
| */ |
| protected SettableBeanProperty(SettableBeanProperty src, String newName) |
| { |
| _propName = newName; |
| _type = src._type; |
| _contextAnnotations = src._contextAnnotations; |
| _valueDeserializer = src._valueDeserializer; |
| _valueTypeDeserializer = src._valueTypeDeserializer; |
| _nullProvider = src._nullProvider; |
| _managedReferenceName = src._managedReferenceName; |
| _propertyIndex = src._propertyIndex; |
| _viewMatcher = src._viewMatcher; |
| } |
| |
| public abstract SettableBeanProperty withValueDeserializer(JsonDeserializer<Object> deser); |
| |
| public abstract SettableBeanProperty withName(String newName); |
| |
| public void setManagedReferenceName(String n) { |
| _managedReferenceName = n; |
| } |
| |
| public void setViews(Class<?>[] views) { |
| if (views == null) { |
| _viewMatcher = null; |
| } else { |
| _viewMatcher = ViewMatcher.construct(views); |
| } |
| } |
| |
| /** |
| * Method used to assign index for property. |
| */ |
| public void assignIndex(int index) { |
| if (_propertyIndex != -1) { |
| throw new IllegalStateException("Property '"+getName()+"' already had index ("+_propertyIndex+"), trying to assign "+index); |
| } |
| _propertyIndex = index; |
| } |
| |
| /* |
| /********************************************************** |
| /* BeanProperty impl |
| /********************************************************** |
| */ |
| |
| @Override |
| public final String getName() { return _propName; } |
| |
| @Override |
| public JavaType getType() { return _type; } |
| |
| @Override |
| public abstract <A extends Annotation> A getAnnotation(Class<A> acls); |
| |
| @Override |
| public abstract AnnotatedMember getMember(); |
| |
| @Override |
| public <A extends Annotation> A getContextAnnotation(Class<A> acls) { |
| return _contextAnnotations.get(acls); |
| } |
| |
| /* |
| /********************************************************** |
| /* Accessors |
| /********************************************************** |
| */ |
| |
| protected final Class<?> getDeclaringClass() { |
| return getMember().getDeclaringClass(); |
| } |
| |
| public String getManagedReferenceName() { return _managedReferenceName; } |
| |
| public boolean hasValueDeserializer() { return (_valueDeserializer != null); } |
| |
| public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); } |
| |
| public JsonDeserializer<Object> getValueDeserializer() { return _valueDeserializer; } |
| |
| public TypeDeserializer getValueTypeDeserializer() { return _valueTypeDeserializer; } |
| |
| public boolean visibleInView(Class<?> activeView) { |
| return (_viewMatcher == null) || _viewMatcher.isVisibleForView(activeView); |
| } |
| |
| public boolean hasViews() { return _viewMatcher != null; } |
| |
| /** |
| * Method for accessing unique index of this property; indexes are |
| * assigned once all properties of a {@link BeanDeserializer} have |
| * been collected. |
| * |
| * @return Index of this property |
| */ |
| public int getPropertyIndex() { return _propertyIndex; } |
| |
| /** |
| * Accessor for id of injectable value, if this bean property supports |
| * value injection. |
| */ |
| public Object getInjectableValueId() { return null; } |
| |
| /* |
| /********************************************************** |
| /* Public API |
| /********************************************************** |
| */ |
| |
| /** |
| * Method called to deserialize appropriate value, given parser (and |
| * context), and set it using appropriate mechanism. |
| * Pre-condition is that passed parser must point to the first token |
| * that should be consumed to produce the value (the only value for |
| * scalars, multiple for Objects and Arrays). |
| */ |
| public abstract void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object instance) |
| throws IOException, JsonProcessingException; |
| |
| public abstract void set(Object instance, Object value) |
| throws IOException; |
| |
| /** |
| * This method is needed by some specialized bean deserializers, |
| * and also called by some {@link #deserializeAndSet} implementations. |
| *<p> |
| * Pre-condition is that passed parser must point to the first token |
| * that should be consumed to produce the value (the only value for |
| * scalars, multiple for Objects and Arrays). |
| */ |
| public final Object deserialize(JsonParser jp, DeserializationContext ctxt) |
| throws IOException, JsonProcessingException |
| { |
| JsonToken t = jp.getCurrentToken(); |
| |
| if (t == JsonToken.VALUE_NULL) { |
| return (_nullProvider == null) ? null : _nullProvider.nullValue(ctxt); |
| } |
| if (_valueTypeDeserializer != null) { |
| return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer); |
| } |
| return _valueDeserializer.deserialize(jp, ctxt); |
| } |
| |
| /* |
| /********************************************************** |
| /* Helper methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Method that takes in exception of any type, and casts or wraps it |
| * to an IOException or its subclass. |
| */ |
| protected void _throwAsIOE(Exception e, Object value) |
| throws IOException |
| { |
| if (e instanceof IllegalArgumentException) { |
| String actType = (value == null) ? "[NULL]" : value.getClass().getName(); |
| StringBuilder msg = new StringBuilder("Problem deserializing property '").append(getName()); |
| msg.append("' (expected type: ").append(getType()); |
| msg.append("; actual type: ").append(actType).append(")"); |
| String origMsg = e.getMessage(); |
| if (origMsg != null) { |
| msg.append(", problem: ").append(origMsg); |
| } else { |
| msg.append(" (no error message provided)"); |
| } |
| throw new JsonMappingException(msg.toString(), null, e); |
| } |
| _throwAsIOE(e); |
| } |
| |
| protected IOException _throwAsIOE(Exception e) |
| throws IOException |
| { |
| if (e instanceof IOException) { |
| throw (IOException) e; |
| } |
| if (e instanceof RuntimeException) { |
| throw (RuntimeException) e; |
| } |
| // let's wrap the innermost problem |
| Throwable th = e; |
| while (th.getCause() != null) { |
| th = th.getCause(); |
| } |
| throw new JsonMappingException(th.getMessage(), null, th); |
| } |
| |
| @Override public String toString() { return "[property '"+getName()+"']"; } |
| |
| /* |
| /********************************************************** |
| /* Implementation classes |
| /********************************************************** |
| */ |
| |
| /** |
| * This concrete sub-class implements property that is set |
| * using regular "setter" method. |
| */ |
| public final static class MethodProperty |
| extends SettableBeanProperty |
| { |
| protected final AnnotatedMethod _annotated; |
| |
| /** |
| * Setter method for modifying property value; used for |
| * "regular" method-accessible properties. |
| */ |
| protected final Method _setter; |
| |
| public MethodProperty(String name, JavaType type, TypeDeserializer typeDeser, |
| Annotations contextAnnotations, AnnotatedMethod method) |
| { |
| super(name, type, typeDeser, contextAnnotations); |
| _annotated = method; |
| _setter = method.getAnnotated(); |
| } |
| |
| protected MethodProperty(MethodProperty src, JsonDeserializer<Object> deser) { |
| super(src, deser); |
| _annotated = src._annotated; |
| _setter = src._setter; |
| } |
| |
| protected MethodProperty(MethodProperty src, String newName) { |
| super(src, newName); |
| _annotated = src._annotated; |
| _setter = src._setter; |
| } |
| |
| @Override |
| public MethodProperty withName(String newName) { |
| return new MethodProperty(this, newName); |
| } |
| |
| @Override |
| public MethodProperty withValueDeserializer(JsonDeserializer<Object> deser) { |
| return new MethodProperty(this, deser); |
| } |
| |
| /* |
| /********************************************************** |
| /* BeanProperty impl |
| /********************************************************** |
| */ |
| |
| @Override |
| public <A extends Annotation> A getAnnotation(Class<A> acls) { |
| return _annotated.getAnnotation(acls); |
| } |
| |
| @Override public AnnotatedMember getMember() { return _annotated; } |
| |
| /* |
| /********************************************************** |
| /* Overridden methods |
| /********************************************************** |
| */ |
| |
| @Override |
| public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object instance) |
| throws IOException, JsonProcessingException |
| { |
| set(instance, deserialize(jp, ctxt)); |
| } |
| |
| @Override |
| public final void set(Object instance, Object value) |
| throws IOException |
| { |
| try { |
| _setter.invoke(instance, value); |
| } catch (Exception e) { |
| _throwAsIOE(e, value); |
| } |
| } |
| } |
| |
| /** |
| * This concrete sub-class implements Collection or Map property that is |
| * indirectly by getting the property value and directly modifying it. |
| */ |
| public final static class SetterlessProperty |
| extends SettableBeanProperty |
| { |
| protected final AnnotatedMethod _annotated; |
| |
| /** |
| * Get method for accessing property value used to access property |
| * (of Collection or Map type) to modify. |
| */ |
| protected final Method _getter; |
| |
| public SetterlessProperty(String name, JavaType type, TypeDeserializer typeDeser, |
| Annotations contextAnnotations, AnnotatedMethod method) |
| { |
| super(name, type, typeDeser, contextAnnotations); |
| _annotated = method; |
| _getter = method.getAnnotated(); |
| } |
| |
| protected SetterlessProperty(SetterlessProperty src, JsonDeserializer<Object> deser) { |
| super(src, deser); |
| _annotated = src._annotated; |
| _getter = src._getter; |
| } |
| |
| protected SetterlessProperty(SetterlessProperty src, String newName) { |
| super(src, newName); |
| _annotated = src._annotated; |
| _getter = src._getter; |
| } |
| |
| @Override |
| public SetterlessProperty withName(String newName) { |
| return new SetterlessProperty(this, newName); |
| } |
| |
| @Override |
| public SetterlessProperty withValueDeserializer(JsonDeserializer<Object> deser) { |
| return new SetterlessProperty(this, deser); |
| } |
| |
| /* |
| /********************************************************** |
| /* BeanProperty impl |
| /********************************************************** |
| */ |
| |
| @Override |
| public <A extends Annotation> A getAnnotation(Class<A> acls) { |
| return _annotated.getAnnotation(acls); |
| } |
| |
| @Override public AnnotatedMember getMember() { return _annotated; } |
| |
| /* |
| /********************************************************** |
| /* Overridden methods |
| /********************************************************** |
| */ |
| |
| @Override |
| public final void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object instance) |
| throws IOException, JsonProcessingException |
| { |
| JsonToken t = jp.getCurrentToken(); |
| if (t == JsonToken.VALUE_NULL) { |
| /* Hmmh. Is this a problem? We won't be setting anything, so it's |
| * equivalent of empty Collection/Map in this case |
| */ |
| return; |
| } |
| |
| // Ok: then, need to fetch Collection/Map to modify: |
| Object toModify; |
| try { |
| toModify = _getter.invoke(instance); |
| } catch (Exception e) { |
| _throwAsIOE(e); |
| return; // never gets here |
| } |
| /* Note: null won't work, since we can't then inject anything |
| * in. At least that's not good in common case. However, |
| * theoretically the case where we get JSON null might |
| * be compatible. If so, implementation could be changed. |
| */ |
| if (toModify == null) { |
| throw new JsonMappingException("Problem deserializing 'setterless' property '"+getName()+"': get method returned null"); |
| } |
| _valueDeserializer.deserialize(jp, ctxt, toModify); |
| } |
| |
| @Override |
| public final void set(Object instance, Object value) |
| throws IOException |
| { |
| throw new UnsupportedOperationException("Should never call 'set' on setterless property"); |
| } |
| } |
| |
| /** |
| * This concrete sub-class implements property that is set |
| * directly assigning to a Field. |
| */ |
| public final static class FieldProperty |
| extends SettableBeanProperty |
| { |
| protected final AnnotatedField _annotated; |
| |
| /** |
| * Actual field to set when deserializing this property. |
| */ |
| protected final Field _field; |
| |
| public FieldProperty(String name, JavaType type, TypeDeserializer typeDeser, |
| Annotations contextAnnotations, AnnotatedField field) |
| { |
| super(name, type, typeDeser, contextAnnotations); |
| _annotated = field; |
| _field = field.getAnnotated(); |
| } |
| |
| protected FieldProperty(FieldProperty src, JsonDeserializer<Object> deser) { |
| super(src, deser); |
| _annotated = src._annotated; |
| _field = src._field; |
| } |
| |
| protected FieldProperty(FieldProperty src, String newName) { |
| super(src, newName); |
| _annotated = src._annotated; |
| _field = src._field; |
| } |
| |
| @Override |
| public FieldProperty withName(String newName) { |
| return new FieldProperty(this, newName); |
| } |
| |
| @Override |
| public FieldProperty withValueDeserializer(JsonDeserializer<Object> deser) { |
| return new FieldProperty(this, deser); |
| } |
| |
| /* |
| /********************************************************** |
| /* BeanProperty impl |
| /********************************************************** |
| */ |
| |
| @Override |
| public <A extends Annotation> A getAnnotation(Class<A> acls) { |
| return _annotated.getAnnotation(acls); |
| } |
| |
| @Override public AnnotatedMember getMember() { return _annotated; } |
| |
| /* |
| /********************************************************** |
| /* Overridden methods |
| /********************************************************** |
| */ |
| |
| @Override |
| public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object instance) |
| throws IOException, JsonProcessingException |
| { |
| set(instance, deserialize(jp, ctxt)); |
| } |
| |
| @Override |
| public final void set(Object instance, Object value) |
| throws IOException |
| { |
| try { |
| _field.set(instance, value); |
| } catch (Exception e) { |
| _throwAsIOE(e, value); |
| } |
| } |
| } |
| |
| /** |
| * Wrapper property that is used to handle managed (forward) properties |
| * (see [JACKSON-235] for more information). Basically just need to |
| * delegate first to actual forward property, and |
| * |
| * @author tatu |
| */ |
| public final static class ManagedReferenceProperty |
| extends SettableBeanProperty |
| { |
| protected final String _referenceName; |
| |
| /** |
| * Flag that indicates whether property to handle is a container type |
| * (array, Collection, Map) or not. |
| */ |
| protected final boolean _isContainer; |
| |
| protected final SettableBeanProperty _managedProperty; |
| |
| protected final SettableBeanProperty _backProperty; |
| |
| public ManagedReferenceProperty(String refName, |
| SettableBeanProperty forward, SettableBeanProperty backward, |
| Annotations contextAnnotations, |
| boolean isContainer) |
| { |
| super(forward.getName(), forward.getType(), forward._valueTypeDeserializer, |
| contextAnnotations); |
| _referenceName = refName; |
| _managedProperty = forward; |
| _backProperty = backward; |
| _isContainer = isContainer; |
| } |
| |
| protected ManagedReferenceProperty(ManagedReferenceProperty src, JsonDeserializer<Object> deser) |
| { |
| super(src, deser); |
| _referenceName = src._referenceName; |
| _isContainer = src._isContainer; |
| _managedProperty = src._managedProperty; |
| _backProperty = src._backProperty; |
| } |
| |
| protected ManagedReferenceProperty(ManagedReferenceProperty src, String newName) { |
| super(src, newName); |
| _referenceName = src._referenceName; |
| _isContainer = src._isContainer; |
| _managedProperty = src._managedProperty; |
| _backProperty = src._backProperty; |
| } |
| |
| @Override |
| public ManagedReferenceProperty withName(String newName) { |
| return new ManagedReferenceProperty(this, newName); |
| } |
| |
| @Override |
| public ManagedReferenceProperty withValueDeserializer(JsonDeserializer<Object> deser) { |
| return new ManagedReferenceProperty(this, deser); |
| } |
| |
| /* |
| /********************************************************** |
| /* BeanProperty impl |
| /********************************************************** |
| */ |
| |
| @Override |
| public <A extends Annotation> A getAnnotation(Class<A> acls) { |
| return _managedProperty.getAnnotation(acls); |
| } |
| |
| @Override public AnnotatedMember getMember() { return _managedProperty.getMember(); } |
| |
| /* |
| /********************************************************** |
| /* Overridden methods |
| /********************************************************** |
| */ |
| |
| @Override |
| public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object instance) |
| throws IOException, JsonProcessingException |
| { |
| set(instance, _managedProperty.deserialize(jp, ctxt)); |
| } |
| |
| @Override |
| public final void set(Object instance, Object value) |
| throws IOException |
| { |
| _managedProperty.set(instance, value); |
| /* And then back reference, if (and only if!) we actually have a non-null |
| * reference |
| */ |
| if (value != null) { |
| if (_isContainer) { // ok, this gets ugly... but has to do for now |
| if (value instanceof Object[]) { |
| for (Object ob : (Object[]) value) { |
| if (ob != null) { |
| _backProperty.set(ob, instance); |
| } |
| } |
| } else if (value instanceof Collection<?>) { |
| for (Object ob : (Collection<?>) value) { |
| if (ob != null) { |
| _backProperty.set(ob, instance); |
| } |
| } |
| } else if (value instanceof Map<?,?>) { |
| for (Object ob : ((Map<?,?>) value).values()) { |
| if (ob != null) { |
| _backProperty.set(ob, instance); |
| } |
| } |
| } else { |
| throw new IllegalStateException("Unsupported container type ("+value.getClass().getName() |
| +") when resolving reference '"+_referenceName+"'"); |
| } |
| } else { |
| _backProperty.set(value, instance); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This sub-class is used to handle special case of value being a |
| * non-static inner class. If so, we will have to use a special |
| * alternative for default constructor; but otherwise can delegate |
| * to regular implementation. |
| */ |
| public final static class InnerClassProperty |
| extends SettableBeanProperty |
| { |
| /** |
| * Actual property that we use after value construction. |
| */ |
| protected final SettableBeanProperty _delegate; |
| |
| /** |
| * Single-arg constructor we use for value instantiation. |
| */ |
| protected final Constructor<?> _creator; |
| |
| public InnerClassProperty(SettableBeanProperty delegate, |
| Constructor<?> ctor) |
| { |
| super(delegate); |
| _delegate = delegate; |
| _creator = ctor; |
| } |
| |
| protected InnerClassProperty(InnerClassProperty src, JsonDeserializer<Object> deser) |
| { |
| super(src, deser); |
| _delegate = src._delegate.withValueDeserializer(deser); |
| _creator = src._creator; |
| } |
| |
| protected InnerClassProperty(InnerClassProperty src, String newName) { |
| super(src, newName); |
| _delegate = src._delegate.withName(newName); |
| _creator = src._creator; |
| } |
| |
| @Override |
| public InnerClassProperty withName(String newName) { |
| return new InnerClassProperty(this, newName); |
| } |
| |
| @Override |
| public InnerClassProperty withValueDeserializer(JsonDeserializer<Object> deser) { |
| return new InnerClassProperty(this, deser); |
| } |
| |
| // // // BeanProperty impl |
| |
| @Override |
| public <A extends Annotation> A getAnnotation(Class<A> acls) { |
| return _delegate.getAnnotation(acls); |
| } |
| |
| @Override public AnnotatedMember getMember() { return _delegate.getMember(); } |
| |
| // // // Overridden methods |
| |
| @Override |
| public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, |
| Object bean) |
| throws IOException, JsonProcessingException |
| { |
| // mostly copied from super class impl: |
| JsonToken t = jp.getCurrentToken(); |
| Object value; |
| if (t == JsonToken.VALUE_NULL) { |
| value = (_nullProvider == null) ? null : _nullProvider.nullValue(ctxt); |
| } else if (_valueTypeDeserializer != null) { |
| value = _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer); |
| } else { // the usual case |
| try { |
| value = _creator.newInstance(bean); |
| } catch (Exception e) { |
| ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+_creator.getDeclaringClass().getName()+", problem: "+e.getMessage()); |
| value = null; |
| } |
| _valueDeserializer.deserialize(jp, ctxt, value); |
| } |
| set(bean, value); |
| } |
| |
| @Override |
| public final void set(Object instance, Object value) throws IOException |
| { |
| _delegate.set(instance, value); |
| } |
| } |
| |
| /* |
| /********************************************************** |
| /* Other helper classes |
| /********************************************************** |
| */ |
| |
| /** |
| * To support [JACKSON-420] we need bit more indirection; this is used to produce |
| * artificial failure for primitives that don't accept JSON null as value. |
| */ |
| protected final static class NullProvider |
| { |
| private final Object _nullValue; |
| |
| private final boolean _isPrimitive; |
| |
| private final Class<?> _rawType; |
| |
| protected NullProvider(JavaType type, Object nullValue) |
| { |
| _nullValue = nullValue; |
| // [JACKSON-420] |
| _isPrimitive = type.isPrimitive(); |
| _rawType = type.getRawClass(); |
| } |
| |
| public Object nullValue(DeserializationContext ctxt) throws JsonProcessingException |
| { |
| if (_isPrimitive && ctxt.isEnabled(DeserializationConfig.Feature.FAIL_ON_NULL_FOR_PRIMITIVES)) { |
| throw ctxt.mappingException("Can not map JSON null into type "+_rawType.getName() |
| +" (set DeserializationConfig.Feature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)"); |
| } |
| return _nullValue; |
| } |
| } |
| } |