package com.fasterxml.jackson.databind.deser; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator; import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; /** * Deserializer only used for abstract types used as placeholders during polymorphic * type handling deserialization. If so, there is no real deserializer associated * with nominal type, just {@link TypeDeserializer}; and any calls that do not * pass such resolver will result in an error. */ public class AbstractDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer, // since 2.9 java.io.Serializable { private static final long serialVersionUID = 1L; protected final JavaType _baseType; protected final ObjectIdReader _objectIdReader; protected final Map<String, SettableBeanProperty> _backRefProperties; protected transient Map<String,SettableBeanProperty> _properties; // support for "native" types, which require special care: protected final boolean _acceptString; protected final boolean _acceptBoolean; protected final boolean _acceptInt; protected final boolean _acceptDouble; /* /********************************************************** /* Life cycle /********************************************************** */ /** * @since 2.9 * * @param props Regular properties: currently only needed to support property-annotated * Object Id handling with property inclusion (needed for determining type of Object Id * to bind) */ public AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps, Map<String, SettableBeanProperty> props) { _baseType = beanDesc.getType(); _objectIdReader = builder.getObjectIdReader(); _backRefProperties = backRefProps; _properties = props; Class<?> cls = _baseType.getRawClass(); _acceptString = cls.isAssignableFrom(String.class); _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); } @Deprecated // since 2.9 public AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) { this(builder, beanDesc, backRefProps, null); } protected AbstractDeserializer(BeanDescription beanDesc) { _baseType = beanDesc.getType(); _objectIdReader = null; _backRefProperties = null; Class<?> cls = _baseType.getRawClass(); _acceptString = cls.isAssignableFrom(String.class); _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); } /** * @since 2.9 */ protected AbstractDeserializer(AbstractDeserializer base, ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props) { _baseType = base._baseType; _backRefProperties = base._backRefProperties; _acceptString = base._acceptString; _acceptBoolean = base._acceptBoolean; _acceptInt = base._acceptInt; _acceptDouble = base._acceptDouble; _objectIdReader = objectIdReader; _properties = props; } /** * Factory method used when constructing instances for non-POJO types, like * {@link java.util.Map}s. * * @since 2.3 */ public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) { return new AbstractDeserializer(beanDesc); } @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (property != null && intr != null) { final AnnotatedMember accessor = property.getMember(); if (accessor != null) { ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) JavaType idType; ObjectIdGenerator<?> idGen; SettableBeanProperty idProp = null; ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); // 2.1: allow modifications by "id ref" annotations as well: objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); Class<?> implClass = objectIdInfo.getGeneratorType(); if (implClass == ObjectIdGenerators.PropertyGenerator.class) { PropertyName propName = objectIdInfo.getPropertyName(); idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName()); if (idProp == null) { ctxt.reportBadDefinition(_baseType, String.format( "Invalid Object Id definition for %s: can not find property with name '%s'", handledType().getName(), propName)); } idType = idProp.getType(); idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); /* ctxt.reportBadDefinition(_baseType, String.format( / "Invalid Object Id definition for abstract type %s: can not use `PropertyGenerator` on polymorphic types using property annotation", handledType().getName())); */ } else { // other types simpler resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); JavaType type = ctxt.constructType(implClass); idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo); } JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType); ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), idGen, deser, idProp, resolver); return new AbstractDeserializer(this, oir, null); } } } if (_properties == null) { return this; } // Need to ensure properties are dropped at this point, regardless return new AbstractDeserializer(this, _objectIdReader, null); } /* /********************************************************** /* Public accessors /********************************************************** */ @Override public Class<?> handledType() { return _baseType.getRawClass(); } @Override public boolean isCachable() { return true; } @Override // since 2.9 public Boolean supportsUpdate(DeserializationConfig config) { /* 23-Oct-2016, tatu: Not exactly sure what to do with this; polymorphic * type handling seems bit risky so for now claim it "may or may not be" * possible, which does allow explicit per-type/per-property merging attempts, * but avoids general-configuration merges */ return null; } /** * Overridden to return true for those instances that are * handling value for which Object Identity handling is enabled * (either via value type or referring property). */ @Override public ObjectIdReader getObjectIdReader() { return _objectIdReader; } /** * Method called by <code>BeanDeserializer</code> to resolve back reference * part of managed references. */ @Override public SettableBeanProperty findBackReference(String logicalName) { return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName); } /* /********************************************************** /* Deserializer implementation /********************************************************** */ @Override public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type? // for now, prefer Object Id: if (_objectIdReader != null) { JsonToken t = p.getCurrentToken(); if (t != null) { // Most commonly, a scalar (int id, uuid String, ...) if (t.isScalarValue()) { return _deserializeFromObjectId(p, ctxt); } // but, with 2.5+, a simple Object-wrapped value also legal: if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { return _deserializeFromObjectId(p, ctxt); } } } // First: support "natural" values (which are always serialized without type info!) Object result = _deserializeIfNatural(p, ctxt); if (result != null) { return result; } return typeDeserializer.deserializeTypedFromObject(p, ctxt); } @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 16-Oct-2016, tatu: Let's pass non-null value instantiator so that we will // get proper exception type; needed to establish there are no creators // (since without ValueInstantiator this would not be known for certain) ValueInstantiator bogus = new ValueInstantiator.Base(_baseType); return ctxt.handleMissingInstantiator(_baseType.getRawClass(), bogus, p, "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information"); } /* /********************************************************** /* Internal methods /********************************************************** */ protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException { /* There is a chance we might be "natural" types * (String, Boolean, Integer, Double), which do not include any type information... * Care must be taken to only return this if return type matches, however. * Finally, we may have to consider possibility of custom handlers for * these values: but for now this should work ok. */ switch (p.getCurrentTokenId()) { case JsonTokenId.ID_STRING: if (_acceptString) { return p.getText(); } break; case JsonTokenId.ID_NUMBER_INT: if (_acceptInt) { return p.getIntValue(); } break; case JsonTokenId.ID_NUMBER_FLOAT: if (_acceptDouble) { return Double.valueOf(p.getDoubleValue()); } break; case JsonTokenId.ID_TRUE: if (_acceptBoolean) { return Boolean.TRUE; } break; case JsonTokenId.ID_FALSE: if (_acceptBoolean) { return Boolean.FALSE; } break; } return null; } /** * Method called in cases where it looks like we got an Object Id * to parse and use as a reference. */ protected Object _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException { Object id = _objectIdReader.readObjectReference(p, ctxt); ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); // do we have it resolved? Object pojo = roid.resolve(); if (pojo == null) { // not yet; should wait... throw new UnresolvedForwardReference(p, "Could not resolve Object Id ["+id+"] -- unresolved forward-reference?", p.getCurrentLocation(), roid); } return pojo; } }