| 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.deser.impl.CreatorCollector; |
| import com.fasterxml.jackson.databind.deser.impl.DeserializerFactoryConfig; |
| 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 |
| { |
| /** |
| * 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.ConcurrentNavigableMap"); |
| Class<?> value = Class.forName("java.util.ConcurrentSkipListMap"); |
| @SuppressWarnings("unchecked") |
| Class<? extends Map<?,?>> mapValue = (Class<? extends Map<?,?>>) value; |
| _mapFallbacks.put(key.getName(), mapValue); |
| } catch (ClassNotFoundException cnfe) { // occurs on 1.5 |
| } |
| } |
| |
| /* 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; |
| } |
| |
| protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config); |
| |
| /* |
| /******************************************************** |
| /* Configuration handling: fluent factories |
| /******************************************************** |
| */ |
| |
| /** |
| * Convenience method for creating a new factory instance with additional deserializer |
| * provider. |
| */ |
| public final DeserializerFactory withAdditionalDeserializers(Deserializers additional) { |
| return withConfig(_factoryConfig.withAdditionalDeserializers(additional)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with additional |
| * {@link KeyDeserializers}. |
| */ |
| public final DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional) { |
| return withConfig(_factoryConfig.withAdditionalKeyDeserializers(additional)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with additional |
| * {@link BeanDeserializerModifier}. |
| */ |
| public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier) { |
| return withConfig(_factoryConfig.withDeserializerModifier(modifier)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with additional |
| * {@link AbstractTypeResolver}. |
| */ |
| public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) { |
| return withConfig(_factoryConfig.withAbstractTypeResolver(resolver)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with additional |
| * {@link ValueInstantiators}. |
| */ |
| public final DeserializerFactory withValueInstantiators(ValueInstantiators instantiators) { |
| return withConfig(_factoryConfig.withValueInstantiators(instantiators)); |
| } |
| |
| /* |
| /********************************************************** |
| /* JsonDeserializerFactory impl (partial): type mappings |
| /********************************************************** |
| */ |
| |
| 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(); |
| |
| // First, let's figure out constructor/factory-based instantiation |
| // 23-Jan-2010, tatus: but only for concrete types |
| if (beanDesc.getType().isConcrete()) { |
| AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor(); |
| if (defaultCtor != null) { |
| if (fixAccess) { |
| ClassUtil.checkAndFixAccess(defaultCtor.getAnnotated()); |
| } |
| creators.setDefaultConstructor(defaultCtor); |
| } |
| } |
| |
| // need to construct suitable visibility checker: |
| final DeserializationConfig config = ctxt.getConfig(); |
| VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker(); |
| vchecker = config.getAnnotationIntrospector().findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker); |
| |
| /* Important: first add factory methods; then constructors, so |
| * latter can override former! |
| */ |
| _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators); |
| _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 |
| { |
| for (AnnotatedConstructor ctor : beanDesc.getConstructors()) { |
| int argCount = ctor.getParameterCount(); |
| if (argCount < 1) { |
| continue; |
| } |
| 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); |
| String name = (param == null) ? null : intr.findDeserializationName(param); |
| 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); |
| String name = intr.findDeserializationName(param); |
| 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()) { |
| int argCount = factory.getParameterCount(); |
| if (argCount < 1) { |
| continue; |
| } |
| boolean isCreator = intr.hasCreatorAnnotation(factory); |
| // some single-arg factory methods (String, number) are auto-detected |
| if (argCount == 1) { |
| AnnotatedParameter param = factory.getParameter(0); |
| String name = intr.findDeserializationName(param); |
| 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); |
| String name = intr.findDeserializationName(param); |
| 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, property); |
| if (type != t0) { |
| property = property.withType(type); |
| } |
| // Is there an annotation that specifies exact deserializer? |
| JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, param, property); |
| // If yes, we are mostly done: |
| type = modifyTypeByAnnotation(ctxt, param, type, property); |
| |
| // 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, BeanProperty property) |
| 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, BeanProperty property) |
| 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, BeanProperty property) |
| 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, BeanProperty property) |
| 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, property); |
| 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, property); |
| } |
| // 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, BeanProperty property) |
| throws JsonMappingException |
| { |
| final DeserializationConfig config = ctxt.getConfig(); |
| BeanDescription beanDesc = config.introspect(type); |
| JsonDeserializer<?> des = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo(), property); |
| 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); |
| 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); |
| 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, BeanProperty property) |
| throws JsonMappingException |
| { |
| Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann); |
| if (deserDef == null) { |
| return null; |
| } |
| return ctxt.deserializerInstance(ann, property, 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 derived from the setter argument |
| * @param prop property that refers to type, if any; null |
| * if no property information available (when modify type declaration |
| * of a class, for example) |
| * |
| * @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, BeanProperty prop) |
| 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, prop, 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, prop, 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, |
| BeanProperty property) |
| 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, property, 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, property, 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(DeserializationConfig.Feature.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(); |
| } |
| } |