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

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

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

import java.io.IOException;
import java.lang.annotation.Annotation;

import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.FailingDeserializer;
import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
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 deserializable properties of a bean: contains
 * both type and name definitions, and reflection-based set functionality.
 * Concrete sub-classes implement details, so that field- and
 * setter-backed properties, as well as a few more esoteric variations,
 * can be handled.
 */
@SuppressWarnings("serial")
public abstract class SettableBeanProperty
    extends ConcreteBeanPropertyBase
    implements java.io.Serializable
{
    /**
     * To avoid nasty NPEs, let's use a placeholder for _valueDeserializer,
     * if real deserializer is not (yet) available.
     * 
     * @since 2.2
     */
    protected static final JsonDeserializer MISSING_VALUE_DESERIALIZER = new FailingDeserializer(
            "No _valueDeserializer assigned");

    /**
     * Logical name of the property (often but not always derived
     * from the setter method name)
     */
    protected final PropertyName _propName;

    /**
     * Base type for property; may be a supertype of actual value.
     */
    protected final JavaType _type;

    /**
     * @since 2.2
     */
    protected final PropertyName _wrapperName;

    /**
     * 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 transient Annotations _contextAnnotations;

    /**
     * Deserializer used for handling property value.
     *

* NOTE: has been immutable since 2.3 */ protected final JsonDeserializer _valueDeserializer; /** * If value will contain type information (to support * polymorphic handling), this is the type deserializer * used to handle type resolution. */ protected final TypeDeserializer _valueTypeDeserializer; /** * Entity used for possible translation from `null` into non-null * value of type of this property. * Often same as _valueDeserializer, but not always. * * @since 2.9 */ protected final NullValueProvider _nullProvider; /* /********************************************************** /* Configuration that is not yet immutable; generally assigned /* during initialization process but cannot be passed to /* constructor. /********************************************************** */ /** * If property represents a managed (forward) reference, we will need * the name of reference for later linking. *

* TODO: should try to make immutable. */ protected String _managedReferenceName; /** * This is the information for object identity associated with the property. *

* TODO: should try to make immutable. */ protected ObjectIdInfo _objectIdInfo; /** * Helper object used for checking whether this property is to * be included in the active view, if property is view-specific; * null otherwise. *

* TODO: should try to make immutable. */ 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. *

* TODO: should try to make immutable if at all possible */ protected int _propertyIndex = -1; /* /********************************************************** /* Life-cycle (construct & configure) /********************************************************** */ protected SettableBeanProperty(BeanPropertyDefinition propDef, JavaType type, TypeDeserializer typeDeser, Annotations contextAnnotations) { this(propDef.getFullName(), type, propDef.getWrapperName(), typeDeser, contextAnnotations, propDef.getMetadata()); } protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyName wrapper, TypeDeserializer typeDeser, Annotations contextAnnotations, PropertyMetadata metadata) { super(metadata); // 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 = PropertyName.NO_NAME; } else { _propName = propName.internSimpleName(); } _type = type; _wrapperName = wrapper; _contextAnnotations = contextAnnotations; _viewMatcher = null; // 30-Jan-2012, tatu: Important: contextualize TypeDeserializer now... if (typeDeser != null) { typeDeser = typeDeser.forProperty(this); } _valueTypeDeserializer = typeDeser; _valueDeserializer = MISSING_VALUE_DESERIALIZER; _nullProvider = MISSING_VALUE_DESERIALIZER; } /** * Constructor only used by {@link com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty}. * * @since 2.3 */ protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyMetadata metadata, JsonDeserializer valueDeser) { super(metadata); // as with above ctor, intern()ing probably fine if (propName == null) { _propName = PropertyName.NO_NAME; } else { _propName = propName.internSimpleName(); } _type = type; _wrapperName = null; _contextAnnotations = null; _viewMatcher = null; _valueTypeDeserializer = null; _valueDeserializer = valueDeser; // 29-Jan-2017, tatu: Presumed to be irrelevant for ObjectId values... _nullProvider = valueDeser; } /** * Basic copy-constructor for sub-classes to use. */ protected SettableBeanProperty(SettableBeanProperty src) { super(src); _propName = src._propName; _type = src._type; _wrapperName = src._wrapperName; _contextAnnotations = src._contextAnnotations; _valueDeserializer = src._valueDeserializer; _valueTypeDeserializer = src._valueTypeDeserializer; _managedReferenceName = src._managedReferenceName; _propertyIndex = src._propertyIndex; _viewMatcher = src._viewMatcher; _nullProvider = src._nullProvider; } /** * Copy-with-deserializer-change constructor for sub-classes to use. */ @SuppressWarnings("unchecked") protected SettableBeanProperty(SettableBeanProperty src, JsonDeserializer deser, NullValueProvider nuller) { super(src); _propName = src._propName; _type = src._type; _wrapperName = src._wrapperName; _contextAnnotations = src._contextAnnotations; _valueTypeDeserializer = src._valueTypeDeserializer; _managedReferenceName = src._managedReferenceName; _propertyIndex = src._propertyIndex; if (deser == null) { _valueDeserializer = MISSING_VALUE_DESERIALIZER; } else { _valueDeserializer = (JsonDeserializer) deser; } _viewMatcher = src._viewMatcher; // 29-Jan-2017, tatu: Bit messy, but for now has to do... if (nuller == MISSING_VALUE_DESERIALIZER) { nuller = _valueDeserializer; } _nullProvider = nuller; } /** * Copy-with-deserializer-change constructor for sub-classes to use. */ protected SettableBeanProperty(SettableBeanProperty src, PropertyName newName) { super(src); _propName = newName; _type = src._type; _wrapperName = src._wrapperName; _contextAnnotations = src._contextAnnotations; _valueDeserializer = src._valueDeserializer; _valueTypeDeserializer = src._valueTypeDeserializer; _managedReferenceName = src._managedReferenceName; _propertyIndex = src._propertyIndex; _viewMatcher = src._viewMatcher; _nullProvider = src._nullProvider; } /** * Fluent factory method for constructing and returning a new instance * with specified value deserializer. * Note that this method should NOT change configuration of this instance. * * @param deser Deserializer to assign to the new property instance * * @return Newly constructed instance, if value deserializer differs from the * one used for this instance; or 'this' if not. */ public abstract SettableBeanProperty withValueDeserializer(JsonDeserializer deser); /** * Fluent factory method for constructing and returning a new instance * with specified property name. * Note that this method should NOT change configuration of this instance. * * @param newName Name to use for the new instance. * * @return Newly constructed instance, if property name differs from the * one used for this instance; or 'this' if not. */ public abstract SettableBeanProperty withName(PropertyName newName); /** * @since 2.3 */ public SettableBeanProperty withSimpleName(String simpleName) { PropertyName n = (_propName == null) ? new PropertyName(simpleName) : _propName.withSimpleName(simpleName); return (n == _propName) ? this : withName(n); } /** * @since 2.9 */ public abstract SettableBeanProperty withNullProvider(NullValueProvider nva); public void setManagedReferenceName(String n) { _managedReferenceName = n; } public void setObjectIdInfo(ObjectIdInfo objectIdInfo) { _objectIdInfo = objectIdInfo; } 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; } /** * Method called to ensure that the mutator has proper access rights to * be called, as per configuration. Overridden by implementations that * have mutators that require access, fields and setters. * * @since 2.8.3 */ public void fixAccess(DeserializationConfig config) { ; } /** * @since 2.9.4 */ public void markAsIgnorable() { } /** * @since 2.9.4 */ public boolean isIgnorable() { return false; } /* /********************************************************** /* BeanProperty impl /********************************************************** */ @Override public final String getName() { return _propName.getSimpleName(); } @Override public PropertyName getFullName() { return _propName; } @Override public JavaType getType() { return _type; } @Override public PropertyName getWrapperName() { return _wrapperName; } @Override public abstract AnnotatedMember getMember(); @Override public abstract A getAnnotation(Class acls); @Override public A getContextAnnotation(Class acls) { return _contextAnnotations.get(acls); } @Override public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException { if (isRequired()) { objectVisitor.property(this); } else { objectVisitor.optionalProperty(this); } } /* /********************************************************** /* Accessors /********************************************************** */ protected Class getDeclaringClass() { return getMember().getDeclaringClass(); } public String getManagedReferenceName() { return _managedReferenceName; } public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; } public boolean hasValueDeserializer() { return (_valueDeserializer != null) && (_valueDeserializer != MISSING_VALUE_DESERIALIZER); } public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); } public JsonDeserializer getValueDeserializer() { JsonDeserializer deser = _valueDeserializer; if (deser == MISSING_VALUE_DESERIALIZER) { return null; } return deser; } public TypeDeserializer getValueTypeDeserializer() { return _valueTypeDeserializer; } /** * @since 2.9 */ public NullValueProvider getNullValueProvider() { return _nullProvider; } 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; } /** * Method for accessing index of the creator property: for other * types of properties will simply return -1. * * @since 2.1 */ public int getCreatorIndex() { // changed from 'return -1' in 2.7.9 / 2.8.7 throw new IllegalStateException(String.format( "Internal error: no creator index for property '%s' (of type %s)", this.getName(), getClass().getName())); } /** * Accessor for id of injectable value, if this bean property supports * value injection. */ public Object getInjectableValueId() { return null; } /** * Accessor for checking whether this property is injectable, and if so, * ONLY injectable (will not bind from input). * Currently (2.11) can only return {@code true} for Creator-backed properties. * * @return True if (and only if) property has injector that is also defined NOT * to bind from input. * * @since 2.11 */ public boolean isInjectionOnly() { return false; } // overridden by CreatorProperty /* /********************************************************** /* 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 p, DeserializationContext ctxt, Object instance) throws IOException; /** * Alternative to {@link #deserializeAndSet} that returns * either return value of setter method called (if one is), * or null to indicate that no return value is available. * Mostly used to support Builder style deserialization. * * @since 2.0 */ public abstract Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException; /** * Method called to assign given value to this property, on * specified Object. *

* Note: this is an optional operation, not supported by all * implementations, creator-backed properties for example do not * support this method. */ public abstract void set(Object instance, Object value) throws IOException; /** * Method called to assign given value to this property, on * specified Object, and return whatever delegating accessor * returned (if anything) *

* Note: this is an optional operation, not supported by all * implementations, creator-backed properties for example do not * support this method. */ public abstract Object setAndReturn(Object instance, Object value) throws IOException; /** * This method is needed by some specialized bean deserializers, * and also called by some {@link #deserializeAndSet} implementations. *

* 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). *

* Note that this method is final for performance reasons: to override * functionality you must override other methods that call this method; * this method should also not be called directly unless you really know * what you are doing (and probably not even then). */ public final Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if (p.hasToken(JsonToken.VALUE_NULL)) { return _nullProvider.getNullValue(ctxt); } if (_valueTypeDeserializer != null) { return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null Object value = _valueDeserializer.deserialize(p, ctxt); if (value == null) { value = _nullProvider.getNullValue(ctxt); } return value; } /** * @since 2.9 */ public final Object deserializeWith(JsonParser p, DeserializationContext ctxt, Object toUpdate) throws IOException { // 20-Oct-2016, tatu: Not 100% sure what to do; probably best to simply return // null value and let caller decide what to do. if (p.hasToken(JsonToken.VALUE_NULL)) { // ... except for "skip nulls" case which should just do that: if (NullsConstantProvider.isSkipper(_nullProvider)) { return toUpdate; } return _nullProvider.getNullValue(ctxt); } if (_valueTypeDeserializer != null) { // 25-Oct-2021 Added by James to support merging polymorphic property // https://github.com/FasterXML/jackson-databind/issues/2541 // Please note we only support merging same type polymorphic property for now, // merging different type is hard and usually doesn't make sense. // Please note you need to configure {@link DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES} as false to // enable this feature otherwise the unknown property exception will be thrown. JavaType subType = ctxt.getTypeFactory().constructType(toUpdate.getClass()); JsonDeserializer subTypeValueDeserializer = ctxt.findContextualValueDeserializer(subType, this); return subTypeValueDeserializer.deserialize(p, ctxt, toUpdate); } // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null Object value = _valueDeserializer.deserialize(p, ctxt, toUpdate); if (value == null) { if (NullsConstantProvider.isSkipper(_nullProvider)) { return toUpdate; } value = _nullProvider.getNullValue(ctxt); } return value; } /* /********************************************************** /* Helper methods /********************************************************** */ /** * Method that takes in exception of any type, and casts or wraps it * to an IOException or its subclass. */ protected void _throwAsIOE(JsonParser p, Exception e, Object value) throws IOException { if (e instanceof IllegalArgumentException) { String actType = ClassUtil.classNameOf(value); StringBuilder msg = new StringBuilder("Problem deserializing property '") .append(getName()) .append("' (expected type: ") .append(getType()) .append("; actual type: ") .append(actType).append(")"); String origMsg = ClassUtil.exceptionMessage(e); if (origMsg != null) { msg.append(", problem: ") .append(origMsg); } else { msg.append(" (no error message provided)"); } throw JsonMappingException.from(p, msg.toString(), e); } _throwAsIOE(p, e); } /** * @since 2.7 */ protected IOException _throwAsIOE(JsonParser p, Exception e) throws IOException { ClassUtil.throwIfIOE(e); ClassUtil.throwIfRTE(e); // let's wrap the innermost problem Throwable th = ClassUtil.getRootCause(e); throw JsonMappingException.from(p, ClassUtil.exceptionMessage(th), th); } @Deprecated // since 2.7 protected IOException _throwAsIOE(Exception e) throws IOException { return _throwAsIOE((JsonParser) null, e); } // 10-Oct-2015, tatu: _Should_ be deprecated, too, but its remaining // callers cannot actually provide a JsonParser protected void _throwAsIOE(Exception e, Object value) throws IOException { _throwAsIOE((JsonParser) null, e, value); } @Override public String toString() { return "[property '"+getName()+"']"; } /* /********************************************************** /* Helper classes /********************************************************** */ /** * Helper class that is designed to both make it easier to sub-class * delegating subtypes and to reduce likelihood of breakage when * new methods are added. *

* Class was specifically added to help with {@code Afterburner} * module, but its use is not limited to only support it. * * @since 2.9 */ public static abstract class Delegating extends SettableBeanProperty { protected final SettableBeanProperty delegate; protected Delegating(SettableBeanProperty d) { super(d); delegate = d; } /** * Method sub-classes must implement, to construct a new instance * with given delegate. */ protected abstract SettableBeanProperty withDelegate(SettableBeanProperty d); protected SettableBeanProperty _with(SettableBeanProperty newDelegate) { if (newDelegate == delegate) { return this; } return withDelegate(newDelegate); } @Override public SettableBeanProperty withValueDeserializer(JsonDeserializer deser) { return _with(delegate.withValueDeserializer(deser)); } @Override public SettableBeanProperty withName(PropertyName newName) { return _with(delegate.withName(newName)); } @Override public SettableBeanProperty withNullProvider(NullValueProvider nva) { return _with(delegate.withNullProvider(nva)); } @Override public void assignIndex(int index) { delegate.assignIndex(index); } @Override public void fixAccess(DeserializationConfig config) { delegate.fixAccess(config); } /* /********************************************************** /* Accessors /********************************************************** */ @Override protected Class getDeclaringClass() { return delegate.getDeclaringClass(); } @Override public String getManagedReferenceName() { return delegate.getManagedReferenceName(); } @Override public ObjectIdInfo getObjectIdInfo() { return delegate.getObjectIdInfo(); } @Override public boolean hasValueDeserializer() { return delegate.hasValueDeserializer(); } @Override public boolean hasValueTypeDeserializer() { return delegate.hasValueTypeDeserializer(); } @Override public JsonDeserializer getValueDeserializer() { return delegate.getValueDeserializer(); } @Override public TypeDeserializer getValueTypeDeserializer() { return delegate.getValueTypeDeserializer(); } @Override public boolean visibleInView(Class activeView) { return delegate.visibleInView(activeView); } @Override public boolean hasViews() { return delegate.hasViews(); } @Override public int getPropertyIndex() { return delegate.getPropertyIndex(); } @Override public int getCreatorIndex() { return delegate.getCreatorIndex(); } @Override public Object getInjectableValueId() { return delegate.getInjectableValueId(); } @Override public boolean isInjectionOnly() { return delegate.isInjectionOnly(); } @Override public AnnotatedMember getMember() { return delegate.getMember(); } @Override public A getAnnotation(Class acls) { return delegate.getAnnotation(acls); } /* /********************************************************** /* Extended API /********************************************************** */ public SettableBeanProperty getDelegate() { return delegate; } /* /********************************************************** /* Actual mutators /********************************************************** */ @Override public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException { delegate.deserializeAndSet(p, ctxt, instance); } @Override public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException { return delegate.deserializeSetAndReturn(p, ctxt, instance); } @Override public void set(Object instance, Object value) throws IOException { delegate.set(instance, value); } @Override public Object setAndReturn(Object instance, Object value) throws IOException { return delegate.setAndReturn(instance, value); } } }