blob: 8ddb4f45d5bc727343b0b39c0f2b0d9889f45d5e [file] [log] [blame]
package com.fasterxml.jackson.databind.jsontype.impl;
import java.io.IOException;
import java.util.HashMap;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* Base class for all standard Jackson {@link TypeDeserializer}s.
*/
public abstract class TypeDeserializerBase
extends TypeDeserializer
implements java.io.Serializable
{
private static final long serialVersionUID = 278445030337366675L;
protected final TypeIdResolver _idResolver;
protected final JavaType _baseType;
/**
* Property that contains value for which type information
* is included; null if value is a root value.
* Note that this value is not assigned during construction
* but only when {@link #forProperty} is called to create
* a copy.
*/
protected final BeanProperty _property;
/**
* Type to use as the default implementation, if type id is
* missing or can not be resolved.
*/
protected final JavaType _defaultImpl;
/**
* Name of type property used; needed for non-property versions too,
* in cases where type id is to be exposed as part of JSON.
*/
protected final String _typePropertyName;
protected final boolean _typeIdVisible;
/**
* For efficient operation we will lazily build mappings from type ids
* to actual deserializers, once needed.
*/
protected final HashMap<String,JsonDeserializer<Object>> _deserializers;
protected JsonDeserializer<Object> _defaultImplDeserializer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected TypeDeserializerBase(JavaType baseType, TypeIdResolver idRes,
String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
{
_baseType = baseType;
_idResolver = idRes;
_typePropertyName = typePropertyName;
_typeIdVisible = typeIdVisible;
_deserializers = new HashMap<String,JsonDeserializer<Object>>();
if (defaultImpl == null) {
_defaultImpl = null;
} else {
/* 16-Oct-2011, tatu: should call this via TypeFactory; this is
* not entirely safe... however, since Collections/Maps are
* seldom (if ever) base types, may be ok.
*/
_defaultImpl = baseType.forcedNarrowBy(defaultImpl);
}
_property = null;
}
protected TypeDeserializerBase(TypeDeserializerBase src, BeanProperty property)
{
_baseType = src._baseType;
_idResolver = src._idResolver;
_typePropertyName = src._typePropertyName;
_typeIdVisible = src._typeIdVisible;
_deserializers = src._deserializers;
_defaultImpl = src._defaultImpl;
_defaultImplDeserializer = src._defaultImplDeserializer;
_property = property;
}
@Override
public abstract TypeDeserializer forProperty(BeanProperty prop);
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
@Override
public abstract JsonTypeInfo.As getTypeInclusion();
public String baseTypeName() { return _baseType.getRawClass().getName(); }
@Override
public final String getPropertyName() { return _typePropertyName; }
@Override
public TypeIdResolver getTypeIdResolver() { return _idResolver; }
@Override
public Class<?> getDefaultImpl() {
return (_defaultImpl == null) ? null : _defaultImpl.getRawClass();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append('[').append(getClass().getName());
sb.append("; base-type:").append(_baseType);
sb.append("; id-resolver: ").append(_idResolver);
sb.append(']');
return sb.toString();
}
/*
/**********************************************************
/* Helper methods for sub-classes
/**********************************************************
*/
protected final JsonDeserializer<Object> _findDeserializer(DeserializationContext ctxt, String typeId)
throws IOException
{
JsonDeserializer<Object> deser;
synchronized (_deserializers) {
deser = _deserializers.get(typeId);
if (deser == null) {
/* As per [Issue#305], need to provide contextual info. But for
* backwards compatibility, let's start by only supporting this
* for base class, not via interface. Later on we can add this
* to the interface, assuming deprecation at base class helps.
*/
JavaType type;
if (_idResolver instanceof TypeIdResolverBase) {
type = ((TypeIdResolverBase) _idResolver).typeFromId(ctxt, typeId);
} else {
type = _idResolver.typeFromId(typeId);
}
if (type == null) {
// As per [JACKSON-614], use the default impl if no type id available:
if (_defaultImpl == null) {
throw ctxt.unknownTypeException(_baseType, typeId);
}
deser = _findDefaultImplDeserializer(ctxt);
} else {
/* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters,
* we actually now need to explicitly narrow from base type (which may have parameterization)
* using raw type.
*
* One complication, though; can not change 'type class' (simple type to container); otherwise
* we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual
* type in process (getting SimpleType of Map.class which will not work as expected)
*/
if (_baseType != null && _baseType.getClass() == type.getClass()) {
type = _baseType.narrowBy(type.getRawClass());
}
deser = ctxt.findContextualValueDeserializer(type, _property);
}
_deserializers.put(typeId, deser);
}
}
return deser;
}
protected final JsonDeserializer<Object> _findDefaultImplDeserializer(DeserializationContext ctxt) throws IOException
{
/* 06-Feb-2013, tatu: As per [Issue#148], consider default implementation value of
* {@link java.lang.Void} to mean "serialize as null"; as well as DeserializationFeature
* to do swift mapping to null
*/
if (_defaultImpl == null) {
if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
return NullifyingDeserializer.instance;
}
return null;
}
Class<?> raw = _defaultImpl.getRawClass();
if (ClassUtil.isBogusClass(raw)) {
return NullifyingDeserializer.instance;
}
synchronized (_defaultImpl) {
if (_defaultImplDeserializer == null) {
_defaultImplDeserializer = ctxt.findContextualValueDeserializer(
_defaultImpl, _property);
}
return _defaultImplDeserializer;
}
}
/**
* Helper method called when {@link JsonParser} indicates that it can use
* so-called native type ids. Assumption from there is that only native
* type ids are to be used.
*
* @since 2.3
*/
@Deprecated
protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException {
return _deserializeWithNativeTypeId(jp, ctxt, jp.getTypeId());
}
/**
* Helper method called when {@link JsonParser} indicates that it can use
* so-called native type ids, and such type id has been found.
*
* @since 2.4
*/
protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId)
throws IOException
{
JsonDeserializer<Object> deser;
if (typeId == null) {
/* 04-May-2014, tatu: Should error be obligatory, or should there be another method
* for "try to deserialize with native tpye id"?
*/
if (_defaultImpl == null) {
throw ctxt.mappingException("No (native) type id found when one was expected for polymorphic type handling");
}
deser = _findDefaultImplDeserializer(ctxt);
} else {
String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId);
deser = _findDeserializer(ctxt, typeIdStr);
}
return deser.deserialize(jp, ctxt);
}
}