package com.fasterxml.jackson.databind.deser;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
import com.fasterxml.jackson.databind.deser.std.*;
import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.*;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.EnumResolver;
/**
* Abstract factory base class that can provide deserializers for standard
* JDK classes, including collection classes and simple heuristics for
* "upcasting" commmon collection interface types
* (such as {@link java.util.Collection}).
*<p>
* Since all simple deserializers are eagerly instantiated, and there is
* no additional introspection or customizability of these types,
* this factory is stateless.
*/
public abstract class BasicDeserializerFactory
extends DeserializerFactory
implements java.io.Serializable
{
private static final long serialVersionUID = 1;
/**
* We will pre-create serializers for common non-structured
* (that is things other than Collection, Map or array)
* types. These need not go through factory.
*/
final protected static HashMap<ClassKey, JsonDeserializer<Object>> _simpleDeserializers
= new HashMap<ClassKey, JsonDeserializer<Object>>();
/**
* Also special array deserializers for primitive array types.
*/
final protected static HashMap<JavaType,JsonDeserializer<Object>> _arrayDeserializers
= PrimitiveArrayDeserializers.getAll();
/**
* Set of available key deserializers is currently limited
* to standard types; and all known instances are storing in this map.
*/
final protected static HashMap<JavaType, KeyDeserializer> _keyDeserializers = StdKeyDeserializers.constructAll();
static {
// First, add the fall-back "untyped" deserializer:
_add(_simpleDeserializers, Object.class, new UntypedObjectDeserializer());
// Then String and String-like converters:
StdDeserializer<?> strDeser = new StringDeserializer();
_add(_simpleDeserializers, String.class, strDeser);
_add(_simpleDeserializers, CharSequence.class, strDeser);
// Primitives/wrappers, other Numbers:
_add(_simpleDeserializers, NumberDeserializers.all());
// Date/time types
_add(_simpleDeserializers, DateDeserializers.all());
// other JDK types
_add(_simpleDeserializers, JdkDeserializers.all());
// and a few Jackson types as well:
_add(_simpleDeserializers, JacksonDeserializers.all());
}
private static void _add(Map<ClassKey, JsonDeserializer<Object>> desers,
StdDeserializer<?>[] serializers) {
for (StdDeserializer<?> ser : serializers) {
_add(desers, ser.getValueClass(), ser);
}
}
private static void _add(Map<ClassKey, JsonDeserializer<Object>> desers,
Class<?> valueClass, StdDeserializer<?> stdDeser)
{
@SuppressWarnings("unchecked")
JsonDeserializer<Object> deser = (JsonDeserializer<Object>) stdDeser;
desers.put(new ClassKey(valueClass), deser);
}
/* We do some defaulting for abstract Map classes and
* interfaces, to avoid having to use exact types or annotations in
* cases where the most common concrete Maps will do.
*/
@SuppressWarnings("rawtypes")
final static HashMap<String, Class<? extends Map>> _mapFallbacks =
new HashMap<String, Class<? extends Map>>();
static {
_mapFallbacks.put(Map.class.getName(), LinkedHashMap.class);
_mapFallbacks.put(ConcurrentMap.class.getName(), ConcurrentHashMap.class);
_mapFallbacks.put(SortedMap.class.getName(), TreeMap.class);
/* 11-Jan-2009, tatu: Let's see if we can still add support for
* JDK 1.6 interfaces, even if we run on 1.5. Just need to be
* more careful with typos, since compiler won't notice any
* problems...
*/
_mapFallbacks.put("java.util.NavigableMap", TreeMap.class);
try {
Class<?> key = Class.forName("java.util.concurrent.ConcurrentNavigableMap");
Class<?> value = Class.forName("java.util.concurrent.ConcurrentSkipListMap");
@SuppressWarnings("unchecked")
Class<? extends Map<?,?>> mapValue = (Class<? extends Map<?,?>>) value;
_mapFallbacks.put(key.getName(), mapValue);
} catch (ClassNotFoundException cnfe) { // occurs on 1.5
} catch (SecurityException se) { // might occur in applets, see stackoverflow.com/questions/12345068
}
}
/* We do some defaulting for abstract Collection classes and
* interfaces, to avoid having to use exact types or annotations in
* cases where the most common concrete Collection will do.
*/
@SuppressWarnings("rawtypes")
final static HashMap<String, Class<? extends Collection>> _collectionFallbacks =
new HashMap<String, Class<? extends Collection>>();
static {
_collectionFallbacks.put(Collection.class.getName(), ArrayList.class);
_collectionFallbacks.put(List.class.getName(), ArrayList.class);
_collectionFallbacks.put(Set.class.getName(), HashSet.class);
_collectionFallbacks.put(SortedSet.class.getName(), TreeSet.class);
_collectionFallbacks.put(Queue.class.getName(), LinkedList.class);
/* 11-Jan-2009, tatu: Let's see if we can still add support for
* JDK 1.6 interfaces, even if we run on 1.5. Just need to be
* more careful with typos, since compiler won't notice any
* problems...
*/
_collectionFallbacks.put("java.util.Deque", LinkedList.class);
_collectionFallbacks.put("java.util.NavigableSet", TreeSet.class);
}
/**
* To support external/optional deserializers, we'll use a helper class
*/
protected OptionalHandlerFactory optionalHandlers = OptionalHandlerFactory.instance;
/*
/**********************************************************
/* Config
/**********************************************************
*/
/**
* Configuration settings for this factory; immutable instance (just like this
* factory), new version created via copy-constructor (fluent-style)
*/
protected final DeserializerFactoryConfig _factoryConfig;
/*
/**********************************************************
/* Life cycle
/**********************************************************
*/
protected BasicDeserializerFactory(DeserializerFactoryConfig config) {
_factoryConfig = config;
}
/**
* Method for getting current {@link DeserializerFactoryConfig}.
*<p>
* Note that since instances are immutable, you can NOT change settings
* by accessing an instance and calling methods: this will simply create
* new instance of config object.
*/
public DeserializerFactoryConfig getFactoryConfig() {
return _factoryConfig;
}
protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config);
/*
/********************************************************
/* Configuration handling: fluent factories
/********************************************************
*/
/**
* Convenience method for creating a new factory instance with additional deserializer
* provider.
*/
@Override
public final DeserializerFactory withAdditionalDeserializers(Deserializers additional) {
return withConfig(_factoryConfig.withAdditionalDeserializers(additional));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link KeyDeserializers}.
*/
@Override
public final DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional) {
return withConfig(_factoryConfig.withAdditionalKeyDeserializers(additional));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link BeanDeserializerModifier}.
*/
@Override
public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier) {
return withConfig(_factoryConfig.withDeserializerModifier(modifier));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link AbstractTypeResolver}.
*/
@Override
public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) {
return withConfig(_factoryConfig.withAbstractTypeResolver(resolver));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link ValueInstantiators}.
*/
@Override
public final DeserializerFactory withValueInstantiators(ValueInstantiators instantiators) {
return withConfig(_factoryConfig.withValueInstantiators(instantiators));
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): type mappings
/**********************************************************
*/
@Override
public JavaType mapAbstractType(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
// first, general mappings
while (true) {
JavaType next = _mapAbstractType2(config, type);
if (next == null) {
return type;
}
/* Should not have to worry about cycles; but better verify since they will invariably
* occur... :-)
* (also: guard against invalid resolution to a non-related type)
*/
Class<?> prevCls = type.getRawClass();
Class<?> nextCls = next.getRawClass();
if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) {
throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former");
}
type = next;
}
}
/**
* Method that will find abstract type mapping for specified type, doing a single
* lookup through registered abstract type resolvers; will not do recursive lookups.
*/
private JavaType _mapAbstractType2(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
Class<?> currClass = type.getRawClass();
if (_factoryConfig.hasAbstractTypeResolvers()) {
for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) {
JavaType concrete = resolver.findTypeMapping(config, type);
if (concrete != null && concrete.getRawClass() != currClass) {
return concrete;
}
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): ValueInstantiators
/**********************************************************
*/
/**
* Value instantiator is created both based on creator annotations,
* and on optional externally provided instantiators (registered through
* module interface).
*/
@Override
public ValueInstantiator findValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
ValueInstantiator instantiator = null;
// [JACKSON-633] Check @JsonValueInstantiator before anything else
AnnotatedClass ac = beanDesc.getClassInfo();
Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ac);
if (instDef != null) {
instantiator = _valueInstantiatorInstance(config, ac, instDef);
}
if (instantiator == null) {
/* Second: see if some of standard Jackson/JDK types might provide value
* instantiators.
*/
instantiator = _findStdValueInstantiator(config, beanDesc);
if (instantiator == null) {
instantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
}
}
// finally: anyone want to modify ValueInstantiator?
if (_factoryConfig.hasValueInstantiators()) {
for (ValueInstantiators insts : _factoryConfig.valueInstantiators()) {
instantiator = insts.findValueInstantiator(config, beanDesc, instantiator);
// let's do sanity check; easier to spot buggy handlers
if (instantiator == null) {
throw new JsonMappingException("Broken registered ValueInstantiators (of type "
+insts.getClass().getName()+"): returned null ValueInstantiator");
}
}
}
return instantiator;
}
private ValueInstantiator _findStdValueInstantiator(DeserializationConfig config,
BeanDescription beanDesc)
throws JsonMappingException
{
return JacksonDeserializers.findValueInstantiator(config, beanDesc);
}
/**
* Method that will construct standard default {@link ValueInstantiator}
* using annotations (like @JsonCreator) and visibility rules
*/
protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
boolean fixAccess = ctxt.canOverrideAccessModifiers();
CreatorCollector creators = new CreatorCollector(beanDesc, fixAccess);
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
// need to construct suitable visibility checker:
final DeserializationConfig config = ctxt.getConfig();
VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker();
vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker);
/* Important: first add factory methods; then constructors, so
* latter can override former!
*/
_addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators);
// constructors only usable on concrete types:
if (beanDesc.getType().isConcrete()) {
_addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators);
}
return creators.constructValueInstantiator(config);
}
public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,
Annotated annotated, Object instDef)
throws JsonMappingException
{
if (instDef == null) {
return null;
}
ValueInstantiator inst;
if (instDef instanceof ValueInstantiator) {
inst = (ValueInstantiator) instDef;
} else {
if (!(instDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+instDef.getClass().getName()
+"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
}
Class<?> instClass = (Class<?>)instDef;
if (instClass == NoClass.class) {
return null;
}
if (!ValueInstantiator.class.isAssignableFrom(instClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "+instClass.getName()
+"; expected Class<ValueInstantiator>");
}
HandlerInstantiator hi = config.getHandlerInstantiator();
if (hi != null) {
inst = hi.valueInstantiatorInstance(config, annotated, instClass);
} else {
inst = (ValueInstantiator) ClassUtil.createInstance(instClass,
config.canOverrideAccessModifiers());
}
}
// not resolvable or contextual, just return:
return inst;
}
protected void _addDeserializerConstructors
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators)
throws JsonMappingException
{
/* First things first: the "default constructor" (zero-arg
* constructor; whether implicit or explicit) is NOT included
* in list of constructors, so needs to be handled separately.
*/
AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
if (defaultCtor != null) {
if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) {
creators.setDefaultCreator(defaultCtor);
}
}
for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
int argCount = ctor.getParameterCount();
boolean isCreator = intr.hasCreatorAnnotation(ctor);
boolean isVisible = vchecker.isCreatorVisible(ctor);
// some single-arg constructors (String, number) are auto-detected
if (argCount == 1) {
_handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
ctor, isCreator, isVisible);
continue;
}
if (!isCreator && !isVisible) {
continue;
}
// [JACKSON-541] improved handling a bit so:
// 2 or more args; all params must have name annotations
// ... or @JacksonInject (or equivalent)
/* [JACKSON-711] One more possibility; can have 1 or more injectables, and
* exactly one non-annotated parameter: if so, it's still delegating.
*/
AnnotatedParameter nonAnnotatedParam = null;
int namedCount = 0;
int injectCount = 0;
CreatorProperty[] properties = new CreatorProperty[argCount];
for (int i = 0; i < argCount; ++i) {
AnnotatedParameter param = ctor.getParameter(i);
PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
String name = (pn == null) ? null : pn.getSimpleName();
Object injectId = intr.findInjectableValueId(param);
if (name != null && name.length() > 0) {
++namedCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (injectId != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (nonAnnotatedParam == null) {
nonAnnotatedParam = param;
}
}
// Ok: if named or injectable, we have more work to do
if (isCreator || namedCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(ctor, properties);
} else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(ctor, properties);
} else { // otherwise, epic fail
throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()+" of constructor "+ctor+" has no property name annotation; must have name when multiple-paramater constructor annotated as Creator");
}
}
}
}
protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
AnnotatedConstructor ctor, boolean isCreator, boolean isVisible)
throws JsonMappingException
{
// note: if we do have parameter name, it'll be "property constructor":
AnnotatedParameter param = ctor.getParameter(0);
PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
String name = (pn == null) ? null : pn.getSimpleName();
Object injectId = intr.findInjectableValueId(param);
if ((injectId != null) || (name != null && name.length() > 0)) { // property-based
// We know there's a name and it's only 1 parameter.
CreatorProperty[] properties = new CreatorProperty[1];
properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, param, injectId);
creators.addPropertyCreator(ctor, properties);
return true;
}
// otherwise either 'simple' number, String, or general delegate:
Class<?> type = ctor.getRawParameterType(0);
if (type == String.class) {
if (isCreator || isVisible) {
creators.addStringCreator(ctor);
}
return true;
}
if (type == int.class || type == Integer.class) {
if (isCreator || isVisible) {
creators.addIntCreator(ctor);
}
return true;
}
if (type == long.class || type == Long.class) {
if (isCreator || isVisible) {
creators.addLongCreator(ctor);
}
return true;
}
if (type == double.class || type == Double.class) {
if (isCreator || isVisible) {
creators.addDoubleCreator(ctor);
}
return true;
}
// Delegating Creator ok iff it has @JsonCreator (etc)
if (isCreator) {
creators.addDelegatingCreator(ctor, null);
return true;
}
return false;
}
protected void _addDeserializerFactoryMethods
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
boolean isCreator = intr.hasCreatorAnnotation(factory);
int argCount = factory.getParameterCount();
// zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850]
if (argCount == 0) {
if (isCreator) {
creators.setDefaultCreator(factory);
}
continue;
}
// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
AnnotatedParameter param = factory.getParameter(0);
PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
String name = (pn == null) ? null : pn.getSimpleName();
Object injectId = intr.findInjectableValueId(param);
if ((injectId == null) && (name == null || name.length() == 0)) { // not property based
_handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
factory, isCreator);
// otherwise just ignored
continue;
}
// fall through if there's name
} else {
// more than 2 args, must be @JsonCreator
if (!intr.hasCreatorAnnotation(factory)) {
continue;
}
}
// 1 or more args; all params must have name annotations
AnnotatedParameter nonAnnotatedParam = null;
CreatorProperty[] properties = new CreatorProperty[argCount];
int namedCount = 0;
int injectCount = 0;
for (int i = 0; i < argCount; ++i) {
AnnotatedParameter param = factory.getParameter(i);
PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
String name = (pn == null) ? null : pn.getSimpleName();
Object injectId = intr.findInjectableValueId(param);
if (name != null && name.length() > 0) {
++namedCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (injectId != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (nonAnnotatedParam == null) {
nonAnnotatedParam = param;
}
}
// Ok: if named or injectable, we have more work to do
if (isCreator || namedCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(factory, properties);
} else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(factory, properties);
} else { // otherwise, epic fail
throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()
+" of factory method "+factory+" has no property name annotation; must have name when multiple-paramater constructor annotated as Creator");
}
}
}
}
protected boolean _handleSingleArgumentFactory(DeserializationConfig config,
BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
AnnotatedMethod factory, boolean isCreator)
throws JsonMappingException
{
Class<?> type = factory.getRawParameterType(0);
if (type == String.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addStringCreator(factory);
}
return true;
}
if (type == int.class || type == Integer.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addIntCreator(factory);
}
return true;
}
if (type == long.class || type == Long.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addLongCreator(factory);
}
return true;
}
if (type == double.class || type == Double.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addDoubleCreator(factory);
}
return true;
}
if (type == boolean.class || type == Boolean.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addBooleanCreator(factory);
}
return true;
}
if (intr.hasCreatorAnnotation(factory)) {
creators.addDelegatingCreator(factory, null);
return true;
}
return false;
}
/**
* Method that will construct a property object that represents
* a logical property passed via Creator (constructor or static
* factory method)
*/
protected CreatorProperty constructCreatorProperty(DeserializationContext ctxt,
BeanDescription beanDesc, String name, int index,
AnnotatedParameter param,
Object injectableValueId)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
JavaType t0 = config.getTypeFactory().constructType(param.getParameterType(), beanDesc.bindingsForBeanType());
BeanProperty.Std property = new BeanProperty.Std(name, t0, beanDesc.getClassAnnotations(), param);
JavaType type = resolveType(ctxt, beanDesc, t0, param);
if (type != t0) {
property = property.withType(type);
}
// Is there an annotation that specifies exact deserializer?
JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, param);
// If yes, we are mostly done:
type = modifyTypeByAnnotation(ctxt, param, type);
// Type deserializer: either comes from property (and already resolved)
TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler();
// or if not, based on type being referenced:
if (typeDeser == null) {
typeDeser = findTypeDeserializer(config, type);
}
CreatorProperty prop = new CreatorProperty(name, type, typeDeser,
beanDesc.getClassAnnotations(), param, index, injectableValueId);
if (deser != null) {
prop = prop.withValueDeserializer(deser);
}
return prop;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl: array deserializers
/**********************************************************
*/
@Override
public JsonDeserializer<?> createArrayDeserializer(DeserializationContext ctxt,
ArrayType type, final BeanDescription beanDesc)
throws JsonMappingException
{
JavaType elemType = type.getContentType();
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer<Object> contentDeser = elemType.getValueHandler();
if (contentDeser == null) {
// Maybe special array type, such as "primitive" arrays (int[] etc)
JsonDeserializer<?> deser = _arrayDeserializers.get(elemType);
if (deser != null) {
/* 23-Nov-2010, tatu: Although not commonly needed, ability to override
* deserializers for all types (including primitive arrays) is useful
* so let's allow this
*/
JsonDeserializer<?> custom = _findCustomArrayDeserializer(type,
ctxt.getConfig(), beanDesc, null, contentDeser);
if (custom != null) {
return custom;
}
return deser;
}
// If not, generic one:
if (elemType.isPrimitive()) { // sanity check
throw new IllegalArgumentException("Internal error: primitive type ("+type+") passed, no array deserializer found");
}
}
// Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
TypeDeserializer elemTypeDeser = elemType.getTypeHandler();
// but if not, may still be possible to find:
if (elemTypeDeser == null) {
elemTypeDeser = findTypeDeserializer(ctxt.getConfig(), elemType);
}
// 23-Nov-2010, tatu: Custom array deserializer?
JsonDeserializer<?> custom = _findCustomArrayDeserializer(type,
ctxt.getConfig(), beanDesc, elemTypeDeser, contentDeser);
if (custom != null) {
return custom;
}
return new ObjectArrayDeserializer(type, contentDeser, elemTypeDeser);
}
protected JsonDeserializer<?> _findCustomArrayDeserializer(ArrayType type,
DeserializationConfig config, BeanDescription beanDesc,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findArrayDeserializer(type, config,
beanDesc, elementTypeDeserializer, elementDeserializer);
if (deser != null) {
return deser;
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl: Collection(-like) deserializers
/**********************************************************
*/
@Override
public JsonDeserializer<?> createCollectionDeserializer(DeserializationContext ctxt,
CollectionType type, BeanDescription beanDesc)
throws JsonMappingException
{
JavaType contentType = type.getContentType();
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer<Object> contentDeser = contentType.getValueHandler();
// Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(ctxt.getConfig(), contentType);
}
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> custom = _findCustomCollectionDeserializer(type,
ctxt.getConfig(), beanDesc, contentTypeDeser, contentDeser);
if (custom != null) {
return custom;
}
Class<?> collectionClass = type.getRawClass();
if (contentDeser == null) { // not defined by annotation
// One special type: EnumSet:
if (EnumSet.class.isAssignableFrom(collectionClass)) {
return new EnumSetDeserializer(contentType, null);
}
}
/* One twist: if we are being asked to instantiate an interface or
* abstract Collection, we need to either find something that implements
* the thing, or give up.
*
* Note that we do NOT try to guess based on secondary interfaces
* here; that would probably not work correctly since casts would
* fail later on (as the primary type is not the interface we'd
* be implementing)
*/
if (type.isInterface() || type.isAbstract()) {
@SuppressWarnings({ "rawtypes" })
Class<? extends Collection> fallback = _collectionFallbacks.get(collectionClass.getName());
if (fallback == null) {
throw new IllegalArgumentException("Can not find a deserializer for non-concrete Collection type "+type);
}
collectionClass = fallback;
type = (CollectionType) ctxt.getConfig().constructSpecializedType(type, collectionClass);
// But if so, also need to re-check creators...
beanDesc = ctxt.getConfig().introspectForCreation(type);
}
ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
// 13-Dec-2010, tatu: Can use more optimal deserializer if content type is String, so:
if (contentType.getRawClass() == String.class) {
// no value type deserializer because Strings are one of natural/native types:
return new StringCollectionDeserializer(type, contentDeser, inst);
}
return new CollectionDeserializer(type, contentDeser, contentTypeDeser, inst);
}
// Copied almost verbatim from "createCollectionDeserializer" -- should try to share more code
@Override
public JsonDeserializer<?> createCollectionLikeDeserializer(DeserializationContext ctxt,
CollectionLikeType type, final BeanDescription beanDesc)
throws JsonMappingException
{
JavaType contentType = type.getContentType();
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer<Object> contentDeser = contentType.getValueHandler();
// Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(ctxt.getConfig(), contentType);
}
return _findCustomCollectionLikeDeserializer(type, ctxt.getConfig(), beanDesc,
contentTypeDeser, contentDeser);
}
protected JsonDeserializer<?> _findCustomCollectionDeserializer(CollectionType type,
DeserializationConfig config, BeanDescription beanDesc,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findCollectionDeserializer(type, config, beanDesc,
elementTypeDeserializer, elementDeserializer);
if (deser != null) {
return deser;
}
}
return null;
}
protected JsonDeserializer<?> _findCustomCollectionLikeDeserializer(CollectionLikeType type,
DeserializationConfig config, BeanDescription beanDesc,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findCollectionLikeDeserializer(type, config, beanDesc,
elementTypeDeserializer, elementDeserializer);
if (deser != null) {
return deser;
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl: Map(-like) deserializers
/**********************************************************
*/
@Override
public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
MapType type, BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
JavaType keyType = type.getKeyType();
JavaType contentType = type.getContentType();
// First: is there annotation-specified deserializer for values?
@SuppressWarnings("unchecked")
JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
// Ok: need a key deserializer (null indicates 'default' here)
KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
// Then optional type info (1.5); either attached to type, or resolved separately:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(config, contentType);
}
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> custom = _findCustomMapDeserializer(type, config, beanDesc,
keyDes, contentTypeDeser, contentDeser);
if (custom != null) {
return custom;
}
// Value handling is identical for all, but EnumMap requires special handling for keys
Class<?> mapClass = type.getRawClass();
if (EnumMap.class.isAssignableFrom(mapClass)) {
Class<?> kt = keyType.getRawClass();
if (kt == null || !kt.isEnum()) {
throw new IllegalArgumentException("Can not construct EnumMap; generic (key) type not available");
}
return new EnumMapDeserializer(type, null, contentDeser);
}
// Otherwise, generic handler works ok.
/* But there is one more twist: if we are being asked to instantiate
* an interface or abstract Map, we need to either find something
* that implements the thing, or give up.
*
* Note that we do NOT try to guess based on secondary interfaces
* here; that would probably not work correctly since casts would
* fail later on (as the primary type is not the interface we'd
* be implementing)
*/
if (type.isInterface() || type.isAbstract()) {
@SuppressWarnings("rawtypes")
Class<? extends Map> fallback = _mapFallbacks.get(mapClass.getName());
if (fallback == null) {
throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type);
}
mapClass = fallback;
type = (MapType) config.constructSpecializedType(type, mapClass);
// But if so, also need to re-check creators...
beanDesc = config.introspectForCreation(type);
}
ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
MapDeserializer md = new MapDeserializer(type, inst, keyDes, contentDeser, contentTypeDeser);
md.setIgnorableProperties(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()));
return md;
}
// Copied almost verbatim from "createMapDeserializer" -- should try to share more code
@Override
public JsonDeserializer<?> createMapLikeDeserializer(DeserializationContext ctxt,
MapLikeType type, final BeanDescription beanDesc)
throws JsonMappingException
{
JavaType keyType = type.getKeyType();
JavaType contentType = type.getContentType();
// First: is there annotation-specified deserializer for values?
@SuppressWarnings("unchecked")
JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
// Ok: need a key deserializer (null indicates 'default' here)
KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
/* !!! 24-Jan-2012, tatu: NOTE: impls MUST use resolve() to find key deserializer!
if (keyDes == null) {
keyDes = p.findKeyDeserializer(config, keyType, property);
}
*/
// Then optional type info (1.5); either attached to type, or resolve separately:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(ctxt.getConfig(), contentType);
}
return _findCustomMapLikeDeserializer(type, ctxt.getConfig(),
beanDesc, keyDes, contentTypeDeser, contentDeser);
}
protected JsonDeserializer<?> _findCustomMapDeserializer(MapType type,
DeserializationConfig config, BeanDescription beanDesc,
KeyDeserializer keyDeserializer,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findMapDeserializer(type, config, beanDesc,
keyDeserializer, elementTypeDeserializer, elementDeserializer);
if (deser != null) {
return deser;
}
}
return null;
}
protected JsonDeserializer<?> _findCustomMapLikeDeserializer(MapLikeType type,
DeserializationConfig config, BeanDescription beanDesc,
KeyDeserializer keyDeserializer,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findMapLikeDeserializer(type, config, beanDesc,
keyDeserializer, elementTypeDeserializer, elementDeserializer);
if (deser != null) {
return deser;
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl: Enum deserializers
/**********************************************************
*/
/**
* Factory method for constructing serializers of {@link Enum} types.
*/
@Override
public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
Class<?> enumClass = type.getRawClass();
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> custom = _findCustomEnumDeserializer(enumClass,
ctxt.getConfig(), beanDesc);
if (custom != null) {
return custom;
}
// [JACKSON-193] May have @JsonCreator for static factory method:
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
int argCount = factory.getParameterCount();
if (argCount == 1) {
Class<?> returnType = factory.getRawReturnType();
// usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
if (returnType.isAssignableFrom(enumClass)) {
return EnumDeserializer.deserializerForCreator(ctxt.getConfig(), enumClass, factory);
}
}
throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type "
+enumClass.getName()+")");
}
}
// [JACKSON-749] Also, need to consider @JsonValue, if one found
return new EnumDeserializer(constructEnumResolver(enumClass, ctxt.getConfig(), beanDesc.findJsonValueMethod()));
}
protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
DeserializationConfig config, BeanDescription beanDesc)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
if (deser != null) {
return deser;
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl: Tree deserializers
/**********************************************************
*/
@Override
public JsonDeserializer<?> createTreeDeserializer(DeserializationConfig config,
JavaType nodeType, BeanDescription beanDesc)
throws JsonMappingException
{
@SuppressWarnings("unchecked")
Class<? extends JsonNode> nodeClass = (Class<? extends JsonNode>) nodeType.getRawClass();
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> custom = _findCustomTreeNodeDeserializer(nodeClass, config,
beanDesc);
if (custom != null) {
return custom;
}
return JsonNodeDeserializer.getDeserializer(nodeClass);
}
protected JsonDeserializer<?> _findCustomTreeNodeDeserializer(Class<? extends JsonNode> type,
DeserializationConfig config, BeanDescription beanDesc)
throws JsonMappingException
{
for (Deserializers d : _factoryConfig.deserializers()) {
JsonDeserializer<?> deser = d.findTreeNodeDeserializer(type, config, beanDesc);
if (deser != null) {
return deser;
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): type deserializers
/**********************************************************
*/
@Override
public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
JavaType baseType)
throws JsonMappingException
{
Class<?> cls = baseType.getRawClass();
BeanDescription bean = config.introspectClassAnnotations(cls);
AnnotatedClass ac = bean.getClassInfo();
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType);
/* Ok: if there is no explicit type info handler, we may want to
* use a default. If so, config object knows what to use.
*/
Collection<NamedType> subtypes = null;
if (b == null) {
b = config.getDefaultTyper(baseType);
if (b == null) {
return null;
}
} else {
subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(ac, config, ai);
}
// [JACKSON-505]: May need to figure out default implementation, if none found yet
// (note: check for abstract type is not 100% mandatory, more of an optimization)
if ((b.getDefaultImpl() == null) && baseType.isAbstract()) {
JavaType defaultType = mapAbstractType(config, baseType);
if (defaultType != null && defaultType.getRawClass() != baseType.getRawClass()) {
b = b.defaultImpl(defaultType.getRawClass());
}
}
return b.buildTypeDeserializer(config, baseType, subtypes);
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): key deserializers
/**********************************************************
*/
@Override
public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
JavaType type)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
if (_factoryConfig.hasKeyDeserializers()) {
BeanDescription beanDesc = config.introspectClassAnnotations(type.getRawClass());
for (KeyDeserializers d : _factoryConfig.keyDeserializers()) {
KeyDeserializer deser = d.findKeyDeserializer(type, config, beanDesc);
if (deser != null) {
return deser;
}
}
}
// and if none found, standard ones:
Class<?> raw = type.getRawClass();
if (raw == String.class || raw == Object.class) {
return StdKeyDeserializers.constructStringKeyDeserializer(config, type);
}
// Most other keys are for limited number of static types
KeyDeserializer kdes = _keyDeserializers.get(type);
if (kdes != null) {
return kdes;
}
// And then other one-offs; first, Enum:
if (type.isEnumType()) {
return _createEnumKeyDeserializer(ctxt, type);
}
// One more thing: can we find ctor(String) or valueOf(String)?
kdes = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type);
return kdes;
}
private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
JavaType type)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
BeanDescription beanDesc = config.introspect(type);
JsonDeserializer<?> des = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
if (des != null) {
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, des);
}
Class<?> enumClass = type.getRawClass();
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> custom = _findCustomEnumDeserializer(enumClass, config, beanDesc);
if (custom != null) {
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, des);
}
EnumResolver<?> enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
// [JACKSON-193] May have @JsonCreator for static factory method:
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
if (config.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
int argCount = factory.getParameterCount();
if (argCount == 1) {
Class<?> returnType = factory.getRawReturnType();
// usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
if (returnType.isAssignableFrom(enumClass)) {
// note: mostly copied from 'EnumDeserializer.deserializerForCreator(...)'
if (factory.getGenericParameterType(0) != String.class) {
throw new IllegalArgumentException("Parameter #0 type for factory method ("+factory+") not suitable, must be java.lang.String");
}
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(factory.getMember());
}
return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes, factory);
}
}
throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type "
+enumClass.getName()+")");
}
}
// [JACKSON-749] Also, need to consider @JsonValue, if one found
return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes);
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* Method called to create a type information deserializer for values of
* given non-container property, if one is needed.
* If not needed (no polymorphic handling configured for property), should return null.
*<p>
* Note that this method is only called for non-container bean properties,
* and not for values in container types or root values (or container properties)
*
* @param baseType Declared base type of the value to deserializer (actual
* deserializer type will be this type or its subtype)
*
* @return Type deserializer to use for given base type, if one is needed; null if not.
*/
public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig config,
JavaType baseType, AnnotatedMember annotated)
throws JsonMappingException
{
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, annotated, baseType);
// Defaulting: if no annotations on member, check value class
if (b == null) {
return findTypeDeserializer(config, baseType);
}
// but if annotations found, may need to resolve subtypes:
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(
annotated, config, ai, baseType);
return b.buildTypeDeserializer(config, baseType, subtypes);
}
/**
* Method called to find and create a type information deserializer for values of
* given container (list, array, map) property, if one is needed.
* If not needed (no polymorphic handling configured for property), should return null.
*<p>
* Note that this method is only called for container bean properties,
* and not for values in container types or root values (or non-container properties)
*
* @param containerType Type of property; must be a container type
* @param propertyEntity Field or method that contains container property
*/
public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfig config,
JavaType containerType, AnnotatedMember propertyEntity)
throws JsonMappingException
{
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType);
JavaType contentType = containerType.getContentType();
// Defaulting: if no annotations on member, check class
if (b == null) {
return findTypeDeserializer(config, contentType);
}
// but if annotations found, may need to resolve subtypes:
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(
propertyEntity, config, ai, contentType);
return b.buildTypeDeserializer(config, contentType, subtypes);
}
/*
/**********************************************************
/* Helper methods, value/content/key type introspection
/**********************************************************
*/
/**
* Helper method called to check if a class or method
* has annotation that tells which class to use for deserialization.
* Returns null if no such annotation found.
*/
protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
Annotated ann)
throws JsonMappingException
{
Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
if (deserDef == null) {
return null;
}
return ctxt.deserializerInstance(ann, deserDef);
}
/**
* Method called to see if given method has annotations that indicate
* a more specific type than what the argument specifies.
* If annotations are present, they must specify compatible Class;
* instance of which can be assigned using the method. This means
* that the Class has to be raw class of type, or its sub-class
* (or, implementing class if original Class instance is an interface).
*
* @param a Method or field that the type is associated with
* @param type Type of field, or the setter argument
*
* @return Original type if no annotations are present; or a more
* specific type derived from it if type annotation(s) was found
*
* @throws JsonMappingException if invalid annotation is found
*/
@SuppressWarnings({ "unchecked" })
protected <T extends JavaType> T modifyTypeByAnnotation(DeserializationContext ctxt,
Annotated a, T type)
throws JsonMappingException
{
// first: let's check class for the instance itself:
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
Class<?> subclass = intr.findDeserializationType(a, type);
if (subclass != null) {
try {
type = (T) type.narrowBy(subclass);
} catch (IllegalArgumentException iae) {
throw new JsonMappingException("Failed to narrow type "+type+" with concrete-type annotation (value "+subclass.getName()+"), method '"+a.getName()+"': "+iae.getMessage(), null, iae);
}
}
// then key class
if (type.isContainerType()) {
Class<?> keyClass = intr.findDeserializationKeyType(a, type.getKeyType());
if (keyClass != null) {
// illegal to use on non-Maps
if (!(type instanceof MapLikeType)) {
throw new JsonMappingException("Illegal key-type annotation: type "+type+" is not a Map(-like) type");
}
try {
type = (T) ((MapLikeType) type).narrowKey(keyClass);
} catch (IllegalArgumentException iae) {
throw new JsonMappingException("Failed to narrow key type "+type+" with key-type annotation ("+keyClass.getName()+"): "+iae.getMessage(), null, iae);
}
}
JavaType keyType = type.getKeyType();
/* 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned)
* (not 100% why or how, but this does seem to get called more than once, which
* is not good: for now, let's just avoid errors)
*/
if (keyType != null && keyType.getValueHandler() == null) {
Object kdDef = intr.findKeyDeserializer(a);
KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef);
if (kd != null) {
type = (T) ((MapLikeType) type).withKeyValueHandler(kd);
keyType = type.getKeyType(); // just in case it's used below
}
}
// and finally content class; only applicable to structured types
Class<?> cc = intr.findDeserializationContentType(a, type.getContentType());
if (cc != null) {
try {
type = (T) type.narrowContentsBy(cc);
} catch (IllegalArgumentException iae) {
throw new JsonMappingException("Failed to narrow content type "+type+" with content-type annotation ("+cc.getName()+"): "+iae.getMessage(), null, iae);
}
}
// ... as well as deserializer for contents:
JavaType contentType = type.getContentType();
if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception)
Object cdDef = intr.findContentDeserializer(a);
JsonDeserializer<?> cd = ctxt.deserializerInstance(a, cdDef);
if (cd != null) {
type = (T) type.withContentValueHandler(cd);
}
}
}
return type;
}
/**
* Helper method used to resolve method return types and field
* types. The main trick here is that the containing bean may
* have type variable binding information (when deserializing
* using generic type passed as type reference), which is
* needed in some cases.
*/
protected JavaType resolveType(DeserializationContext ctxt,
BeanDescription beanDesc, JavaType type, AnnotatedMember member)
throws JsonMappingException
{
// [JACKSON-154]: Also need to handle keyUsing, contentUsing
if (type.isContainerType()) {
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
JavaType keyType = type.getKeyType();
if (keyType != null) {
Object kdDef = intr.findKeyDeserializer(member);
KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef);
if (kd != null) {
type = ((MapLikeType) type).withKeyValueHandler(kd);
keyType = type.getKeyType(); // just in case it's used below
}
}
// and all container types have content types...
Object cdDef = intr.findContentDeserializer(member);
JsonDeserializer<?> cd = ctxt.deserializerInstance(member, cdDef);
if (cd != null) {
type = type.withContentValueHandler(cd);
}
/* 04-Feb-2010, tatu: Need to figure out JAXB annotations that indicate type
* information to use for polymorphic members; and specifically types for
* collection values (contents).
* ... but only applies to members (fields, methods), not classes
*/
if (member instanceof AnnotatedMember) {
TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer(
ctxt.getConfig(), type, (AnnotatedMember) member);
if (contentTypeDeser != null) {
type = type.withContentTypeHandler(contentTypeDeser);
}
}
}
TypeDeserializer valueTypeDeser;
if (member instanceof AnnotatedMember) { // JAXB allows per-property annotations
valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(),
type, (AnnotatedMember) member);
} else { // classes just have Jackson annotations
// probably only occurs if 'property' is null anyway
valueTypeDeser = findTypeDeserializer(ctxt.getConfig(), type);
}
if (valueTypeDeser != null) {
type = type.withTypeHandler(valueTypeDeser);
}
return type;
}
protected EnumResolver<?> constructEnumResolver(Class<?> enumClass,
DeserializationConfig config, AnnotatedMethod jsonValueMethod)
{
if (jsonValueMethod != null) {
Method accessor = jsonValueMethod.getAnnotated();
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(accessor);
}
return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor);
}
// [JACKSON-212]: may need to use Enum.toString()
if (config.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)) {
return EnumResolver.constructUnsafeUsingToString(enumClass);
}
return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
}
protected AnnotatedMethod _findJsonValueFor(DeserializationConfig config, JavaType enumType)
{
if (enumType == null) {
return null;
}
BeanDescription beanDesc = config.introspect(enumType);
return beanDesc.findJsonValueMethod();
}
}