package com.fasterxml.jackson.databind.deser;
import java.lang.reflect.
Method;
import java.util.*;
import java.util.concurrent.*;
import com.fasterxml.jackson.
core.
JsonLocation;
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.
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;
import com.fasterxml.jackson.databind.
util.
NameTransformer;
import com.fasterxml.jackson.databind.
util.
TokenBuffer;
/**
* Abstract factory base class that can provide deserializers for standard
* JDK classes, including collection classes and simple heuristics for
* "upcasting" common 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.
*/
@
SuppressWarnings("serial")
public abstract class
BasicDeserializerFactory
extends
DeserializerFactory
implements java.io.
Serializable
{
private final static
Class<?>
CLASS_OBJECT =
Object.class;
private final static
Class<?>
CLASS_STRING =
String.class;
private final static
Class<?>
CLASS_CHAR_BUFFER =
CharSequence.class;
private final static
Class<?>
CLASS_ITERABLE =
Iterable.class;
/**
* We need a placeholder for creator properties that don't have name
* but are marked with `@JsonWrapped` annotation.
*/
protected final static
PropertyName UNWRAPPED_CREATOR_PARAM_NAME = new
PropertyName("@JsonUnwrapped");
/* 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 = java.util.concurrent.
ConcurrentNavigableMap.class;
Class<?>
value = java.util.concurrent.
ConcurrentSkipListMap.class;
@
SuppressWarnings("unchecked")
Class<? extends
Map<?,?>>
mapValue = (
Class<? extends
Map<?,?>>)
value;
_mapFallbacks.
put(
key.
getName(),
mapValue);
} catch (
Throwable e) { // some class loading problems are Errors, others Exceptions
System.
err.
println("Problems with (optional) types: "+
e);
}
}
/* 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);
// then 1.6 types:
/* 17-May-2013, tatu: [Issue#216] Should be fine to use straight Class references EXCEPT
* that some godforsaken platforms (... looking at you, Android) do not
* include these. So, use "soft" references...
*/
_collectionFallbacks.
put("java.util.Deque",
LinkedList.class);
_collectionFallbacks.
put("java.util.NavigableSet",
TreeSet.class);
}
/*
/**********************************************************
/* 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");
}
}
}
// Sanity check: does the chosen instantatior have incomplete creators?
if (
instantiator.
getIncompleteParameter() != null) {
final
AnnotatedParameter nonAnnotatedParam =
instantiator.
getIncompleteParameter();
final
AnnotatedWithParams ctor =
nonAnnotatedParam.
getOwner();
throw new
IllegalArgumentException("Argument #"+
nonAnnotatedParam.
getIndex()+" of constructor "+
ctor+" has no property name annotation; must have name when multiple-paramater constructor annotated as Creator");
}
return
instantiator;
}
private
ValueInstantiator _findStdValueInstantiator(
DeserializationConfig config,
BeanDescription beanDesc)
throws
JsonMappingException
{
if (
beanDesc.
getBeanClass() ==
JsonLocation.class) {
return
JsonLocationInstantiator.
instance;
}
return null;
}
/**
* 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) {
return (
ValueInstantiator)
instDef;
}
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);
if (
inst != null) {
return
inst;
}
}
return (
ValueInstantiator)
ClassUtil.
createInstance(
instClass,
config.
canOverrideAccessModifiers());
}
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);
}
}
PropertyName[]
ctorPropNames = null;
AnnotatedConstructor propertyCtor = null;
for (
BeanPropertyDefinition propDef :
beanDesc.
findProperties()) {
if (
propDef.
getConstructorParameter() != null) {
AnnotatedParameter param =
propDef.
getConstructorParameter();
AnnotatedWithParams owner =
param.
getOwner();
if (
owner instanceof
AnnotatedConstructor) {
if (
propertyCtor == null) {
propertyCtor = (
AnnotatedConstructor)
owner;
ctorPropNames = new
PropertyName[
propertyCtor.
getParameterCount()];
}
ctorPropNames[
param.
getIndex()] =
propDef.
getFullName();
}
}
}
for (
AnnotatedConstructor ctor :
beanDesc.
getConstructors()) {
int
argCount =
ctor.
getParameterCount();
boolean
isCreator =
intr.
hasCreatorAnnotation(
ctor) ||
ctor ==
propertyCtor;
boolean
isVisible =
vchecker.
isCreatorVisible(
ctor);
// some single-arg constructors (String, number) are auto-detected
if (
argCount == 1) {
PropertyName name = (
ctor ==
propertyCtor) ?
ctorPropNames[0] : null;
_handleSingleArgumentConstructor(
ctxt,
beanDesc,
vchecker,
intr,
creators,
ctor,
isCreator,
isVisible,
name);
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 name = null;
if (
ctor ==
propertyCtor) {
name =
ctorPropNames[
i];
}
if (
name == null) {
name = (
param == null) ? null :
intr.
findNameForDeserialization(
param);
}
Object injectId =
intr.
findInjectableValueId(
param);
if (
name != null &&
name.
hasSimpleName()) {
++
namedCount;
properties[
i] =
constructCreatorProperty(
ctxt,
beanDesc,
name,
i,
param,
injectId);
} else if (
injectId != null) { // injectable
++
injectCount;
properties[
i] =
constructCreatorProperty(
ctxt,
beanDesc,
name,
i,
param,
injectId);
} else {
NameTransformer unwrapper =
intr.
findUnwrappingNameTransformer(
param);
if (
unwrapper != null) { // [Issue#265]: allow unwrapped properties
properties[
i] =
constructCreatorProperty(
ctxt,
beanDesc,
UNWRAPPED_CREATOR_PARAM_NAME,
i,
param, null);
++
namedCount;
} 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, record the incomplete parameter for later error messaging.
creators.
addIncompeteParameter(
nonAnnotatedParam);
}
}
}
}
protected boolean _handleSingleArgumentConstructor(
DeserializationContext ctxt,
BeanDescription beanDesc,
VisibilityChecker<?>
vchecker,
AnnotationIntrospector intr,
CreatorCollector creators,
AnnotatedConstructor ctor, boolean
isCreator, boolean
isVisible,
PropertyName name)
throws
JsonMappingException
{
// note: if we do have parameter name, it'll be "property constructor":
AnnotatedParameter param =
ctor.
getParameter(0);
if (
name == null) {
name = (
param == null) ? null :
intr.
findNameForDeserialization(
param);
}
Object injectId =
intr.
findInjectableValueId(
param);
if ((
injectId != null) || (
name != null &&
name.
hasSimpleName())) { // 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 name = (
param == null) ? null :
intr.
findNameForDeserialization(
param);
Object injectId =
intr.
findInjectableValueId(
param);
if (
name != null &&
name.
hasSimpleName()) {
++
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 {
NameTransformer unwrapper =
intr.
findUnwrappingNameTransformer(
param);
if (
unwrapper != null) {
properties[
i] =
constructCreatorProperty(
ctxt,
beanDesc,
UNWRAPPED_CREATOR_PARAM_NAME,
i,
param, null);
++
namedCount;
} 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,
PropertyName name, int
index,
AnnotatedParameter param,
Object injectableValueId)
throws
JsonMappingException
{
final
DeserializationConfig config =
ctxt.
getConfig();
final
AnnotationIntrospector intr =
ctxt.
getAnnotationIntrospector();
PropertyMetadata metadata;
{
Boolean b = (
intr == null) ? null :
intr.
hasRequiredMarker(
param);
boolean
req = (
b != null &&
b.
booleanValue());
String desc = (
intr == null) ? null :
intr.
findPropertyDescription(
param);
metadata =
PropertyMetadata.
construct(
req,
desc);
}
JavaType t0 =
config.
getTypeFactory().
constructType(
param.
getParameterType(),
beanDesc.
bindingsForBeanType());
BeanProperty.
Std property = new
BeanProperty.
Std(
name,
t0,
intr.
findWrapperName(
param),
beanDesc.
getClassAnnotations(),
param,
metadata);
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,
property.
getWrapperName(),
typeDeser,
beanDesc.
getClassAnnotations(),
param,
index,
injectableValueId,
metadata);
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
{
final
DeserializationConfig config =
ctxt.
getConfig();
JavaType elemType =
type.
getContentType();
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer<
Object>
contentDeser =
elemType.
getValueHandler();
// 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(
config,
elemType);
}
// 23-Nov-2010, tatu: Custom array deserializer?
JsonDeserializer<?>
deser =
_findCustomArrayDeserializer(
type,
config,
beanDesc,
elemTypeDeser,
contentDeser);
if (
deser == null) {
if (
contentDeser == null) {
Class<?>
raw =
elemType.
getRawClass();
if (
elemType.
isPrimitive()) {
return
PrimitiveArrayDeserializers.
forType(
raw);
} else if (
raw ==
String.class) {
return
StringArrayDeserializer.
instance;
}
}
deser = new
ObjectArrayDeserializer(
type,
contentDeser,
elemTypeDeser);
}
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyArrayDeserializer(
config,
type,
beanDesc,
deser);
}
}
return
deser;
}
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();
final
DeserializationConfig config =
ctxt.
getConfig();
// 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(
config,
contentType);
}
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?>
deser =
_findCustomCollectionDeserializer(
type,
config,
beanDesc,
contentTypeDeser,
contentDeser);
if (
deser == null) {
Class<?>
collectionClass =
type.
getRawClass();
if (
contentDeser == null) { // not defined by annotation
// One special type: EnumSet:
if (
EnumSet.class.
isAssignableFrom(
collectionClass)) {
deser = 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 (
deser == null) {
if (
type.
isInterface() ||
type.
isAbstract()) {
CollectionType implType =
_mapAbstractCollectionType(
type,
config);
if (
implType == null) {
// [Issue#292]: Actually, may be fine, but only if polymorphich deser enabled
if (
type.
getTypeHandler() == null) {
throw new
IllegalArgumentException("Can not find a deserializer for non-concrete Collection type "+
type);
}
deser =
AbstractDeserializer.
constructForNonPOJO(
beanDesc);
} else {
type =
implType;
// But if so, also need to re-check creators...
beanDesc =
config.
introspectForCreation(
type);
}
}
if (
deser == null) {
ValueInstantiator inst =
findValueInstantiator(
ctxt,
beanDesc);
if (!
inst.
canCreateUsingDefault()) {
// [Issue#161]: No default constructor for ArrayBlockingQueue...
if (
type.
getRawClass() ==
ArrayBlockingQueue.class) {
return new
ArrayBlockingQueueDeserializer(
type,
contentDeser,
contentTypeDeser,
inst, null);
}
}
// 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:
deser = new
StringCollectionDeserializer(
type,
contentDeser,
inst);
} else {
deser = new
CollectionDeserializer(
type,
contentDeser,
contentTypeDeser,
inst);
}
}
}
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyCollectionDeserializer(
config,
type,
beanDesc,
deser);
}
}
return
deser;
}
protected
CollectionType _mapAbstractCollectionType(
JavaType type,
DeserializationConfig config)
{
Class<?>
collectionClass =
type.
getRawClass();
collectionClass =
_collectionFallbacks.
get(
collectionClass.
getName());
if (
collectionClass == null) {
return null;
}
return (
CollectionType)
config.
constructSpecializedType(
type,
collectionClass);
}
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;
}
// 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();
final
DeserializationConfig config =
ctxt.
getConfig();
// 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(
config,
contentType);
}
JsonDeserializer<?>
deser =
_findCustomCollectionLikeDeserializer(
type,
config,
beanDesc,
contentTypeDeser,
contentDeser);
if (
deser != null) {
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyCollectionLikeDeserializer(
config,
type,
beanDesc,
deser);
}
}
}
return
deser;
}
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<?>
deser =
_findCustomMapDeserializer(
type,
config,
beanDesc,
keyDes,
contentTypeDeser,
contentDeser);
if (
deser == null) {
// 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");
}
deser = new
EnumMapDeserializer(
type, null,
contentDeser,
contentTypeDeser);
}
// 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 (
deser == null) {
if (
type.
isInterface() ||
type.
isAbstract()) {
@
SuppressWarnings("rawtypes")
Class<? extends
Map>
fallback =
_mapFallbacks.
get(
mapClass.
getName());
if (
fallback != null) {
mapClass =
fallback;
type = (
MapType)
config.
constructSpecializedType(
type,
mapClass);
// But if so, also need to re-check creators...
beanDesc =
config.
introspectForCreation(
type);
} else {
// [Issue#292]: Actually, may be fine, but only if polymorphich deser enabled
if (
type.
getTypeHandler() == null) {
throw new
IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+
type);
}
deser =
AbstractDeserializer.
constructForNonPOJO(
beanDesc);
}
}
if (
deser == null) {
ValueInstantiator inst =
findValueInstantiator(
ctxt,
beanDesc);
MapDeserializer md = new
MapDeserializer(
type,
inst,
keyDes,
contentDeser,
contentTypeDeser);
md.
setIgnorableProperties(
config.
getAnnotationIntrospector().
findPropertiesToIgnore(
beanDesc.
getClassInfo()));
deser =
md;
}
}
}
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyMapDeserializer(
config,
type,
beanDesc,
deser);
}
}
return
deser;
}
// 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();
final
DeserializationConfig config =
ctxt.
getConfig();
// 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(
config,
contentType);
}
JsonDeserializer<?>
deser =
_findCustomMapLikeDeserializer(
type,
config,
beanDesc,
keyDes,
contentTypeDeser,
contentDeser);
if (
deser != null) {
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyMapLikeDeserializer(
config,
type,
beanDesc,
deser);
}
}
}
return
deser;
}
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
{
final
DeserializationConfig config =
ctxt.
getConfig();
final
Class<?>
enumClass =
type.
getRawClass();
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?>
deser =
_findCustomEnumDeserializer(
enumClass,
config,
beanDesc);
if (
deser == null) {
// [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)) {
deser =
EnumDeserializer.
deserializerForCreator(
config,
enumClass,
factory);
break;
}
}
throw new
IllegalArgumentException("Unsuitable method ("+
factory+") decorated with @JsonCreator (for Enum type "
+
enumClass.
getName()+")");
}
}
// [JACKSON-749] Also, need to consider @JsonValue, if one found
if (
deser == null) {
deser = new
EnumDeserializer(
constructEnumResolver(
enumClass,
config,
beanDesc.
findJsonValueMethod()));
}
}
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyEnumDeserializer(
config,
type,
beanDesc,
deser);
}
}
return
deser;
}
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();
KeyDeserializer deser = null;
if (
_factoryConfig.
hasKeyDeserializers()) {
BeanDescription beanDesc =
config.
introspectClassAnnotations(
type.
getRawClass());
for (
KeyDeserializers d :
_factoryConfig.
keyDeserializers()) {
deser =
d.
findKeyDeserializer(
type,
config,
beanDesc);
if (
deser != null) {
break;
}
}
}
// the only non-standard thing is this:
if (
deser == null) {
if (
type.
isEnumType()) {
return
_createEnumKeyDeserializer(
ctxt,
type);
}
deser =
StdKeyDeserializers.
findStringBasedKeyDeserializer(
config,
type);
}
// and then new with 2.2: ability to post-process it too (Issue#120)
if (
deser != null) {
if (
_factoryConfig.
hasDeserializerModifiers()) {
for (
BeanDeserializerModifier mod :
_factoryConfig.
deserializerModifiers()) {
deser =
mod.
modifyKeyDeserializer(
config,
type,
deser);
}
}
}
return
deser;
}
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,
custom);
}
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 method called to find one of default serializers for "well-known"
* platform types: JDK-provided types, and small number of public Jackson
* API types.
*
* @since 2.2
*/
public
JsonDeserializer<?> findDefaultDeserializer(
DeserializationContext ctxt,
JavaType type,
BeanDescription beanDesc)
throws
JsonMappingException
{
Class<?>
rawType =
type.
getRawClass();
String clsName =
rawType.
getName();
if (
rawType.
isPrimitive() ||
clsName.
startsWith("java.")) {
// Object ("untyped"), String equivalents:
if (
rawType ==
CLASS_OBJECT) {
return new
UntypedObjectDeserializer();
}
if (
rawType ==
CLASS_STRING ||
rawType ==
CLASS_CHAR_BUFFER) {
return
StringDeserializer.
instance;
}
if (
rawType ==
CLASS_ITERABLE) {
// [Issue#199]: Can and should 'upgrade' to a Collection type:
TypeFactory tf =
ctxt.
getTypeFactory();
JavaType elemType = (
type.
containedTypeCount() > 0) ?
type.
containedType(0) :
TypeFactory.
unknownType();
CollectionType ct =
tf.
constructCollectionType(
Collection.class,
elemType);
// Should we re-introspect beanDesc? For now let's not...
return
createCollectionDeserializer(
ctxt,
ct,
beanDesc);
}
// Primitives/wrappers, other Numbers:
JsonDeserializer<?>
deser =
NumberDeserializers.
find(
rawType,
clsName);
if (
deser == null) {
deser =
DateDeserializers.
find(
rawType,
clsName);
if (
deser == null) {
deser =
JdkDeserializers.
find(
rawType,
clsName);
}
}
return
deser;
}
if (
clsName.
startsWith("com.fasterxml.")) {
// and a few Jackson types as well:
if (
rawType ==
TokenBuffer.class) {
return
TokenBufferDeserializer.
instance;
}
if (
JavaType.class.
isAssignableFrom(
rawType)) {
return
JavaTypeDeserializer.
instance;
}
}
return null;
}
/*
/**********************************************************
/* 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();
}
}