All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.groupbyinc.common.jackson.databind.deser.BeanDeserializerFactory Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
package com.fasterxml.jackson.databind.deser;

import java.util.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition;

/**
 * Concrete deserializer factory class that adds full Bean deserializer
 * construction logic using class introspection.
 * Note that factories specifically do not implement any form of caching:
 * aside from configuration they are stateless; caching is implemented
 * by other components.
 *

* Instances of this class are fully immutable as all configuration is * done by using "fluent factories" (methods that construct new factory * instances with different configuration, instead of modifying instance). */ public class BeanDeserializerFactory extends BasicDeserializerFactory implements java.io.Serializable // since 2.1 { private static final long serialVersionUID = 1; /** * Signature of Throwable.initCause method. */ private final static Class[] INIT_CAUSE_PARAMS = new Class[] { Throwable.class }; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * Globally shareable thread-safe instance which has no additional custom deserializers * registered */ public final static BeanDeserializerFactory instance = new BeanDeserializerFactory( new DeserializerFactoryConfig()); public BeanDeserializerFactory(DeserializerFactoryConfig config) { super(config); } /** * Method used by module registration functionality, to construct a new bean * deserializer factory * with different configuration settings. */ @Override public DeserializerFactory withConfig(DeserializerFactoryConfig config) { if (_factoryConfig == config) { return this; } /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor; * and we pretty much have to here either choose between losing subtype instance * when registering additional deserializers, or losing deserializers. * Instead, let's actually just throw an error if this method is called when subtype * has not properly overridden this method; this to indicate problem as soon as possible. */ ClassUtil.verifyMustOverride(BeanDeserializerFactory.class, this, "withConfig"); return new BeanDeserializerFactory(config); } /* /********************************************************** /* DeserializerFactory API implementation /********************************************************** */ /** * Method that {@link DeserializerCache}s call to create a new * deserializer for types other than Collections, Maps, arrays and * enums. */ @Override public JsonDeserializer createBeanDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { final DeserializationConfig config = ctxt.getConfig(); // We may also have custom overrides: JsonDeserializer custom = _findCustomBeanDeserializer(type, config, beanDesc); if (custom != null) { return custom; } /* One more thing to check: do we have an exception type * (Throwable or its sub-classes)? If so, need slightly * different handling. */ if (type.isThrowable()) { return buildThrowableDeserializer(ctxt, type, beanDesc); } // Or, for abstract types, may have alternate means for resolution // (defaulting, materialization) // 29-Nov-2015, tatu: Also, filter out calls to primitive types, they are // not something we could materialize anything for if (type.isAbstract() && !type.isPrimitive() && !type.isEnumType()) { // Let's make it possible to materialize abstract types. JavaType concreteType = materializeAbstractType(ctxt, type, beanDesc); if (concreteType != null) { /* important: introspect actual implementation (abstract class or * interface doesn't have constructors, for one) */ beanDesc = config.introspect(concreteType); return buildBeanDeserializer(ctxt, concreteType, beanDesc); } } // Otherwise, may want to check handlers for standard types, from superclass: @SuppressWarnings("unchecked") JsonDeserializer deser = (JsonDeserializer) findStdDeserializer(ctxt, type, beanDesc); if (deser != null) { return deser; } // Otherwise: could the class be a Bean class? If not, bail out if (!isPotentialBeanType(type.getRawClass())) { return null; } // For checks like [databind#1599] _validateSubType(ctxt, type, beanDesc); // Use generic bean introspection to build deserializer return buildBeanDeserializer(ctxt, type, beanDesc); } @Override public JsonDeserializer createBuilderBasedDeserializer(DeserializationContext ctxt, JavaType valueType, BeanDescription beanDesc, Class builderClass) throws JsonMappingException { // First: need a BeanDescription for builder class JavaType builderType = ctxt.constructType(builderClass); BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType); return buildBuilderBasedDeserializer(ctxt, valueType, builderDesc); } /** * Method called by {@link BeanDeserializerFactory} to see if there might be a standard * deserializer registered for given type. */ protected JsonDeserializer findStdDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { // note: we do NOT check for custom deserializers here, caller has already // done that JsonDeserializer deser = findDefaultDeserializer(ctxt, type, beanDesc); // Also: better ensure these are post-processable? if (deser != null) { if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { deser = mod.modifyDeserializer(ctxt.getConfig(), beanDesc, deser); } } } return deser; } protected JavaType materializeAbstractType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { // May have multiple resolvers, call in precedence order until one returns non-null for (AbstractTypeResolver r : _factoryConfig.abstractTypeResolvers()) { JavaType concrete = r.resolveAbstractType(ctxt.getConfig(), beanDesc); if (concrete != null) { return concrete; } } return null; } /* /********************************************************** /* Public construction method beyond DeserializerFactory API: /* can be called from outside as well as overridden by /* sub-classes /********************************************************** */ /** * Method that is to actually build a bean deserializer instance. * All basic sanity checks have been done to know that what we have * may be a valid bean type, and that there are no default simple * deserializers. */ @SuppressWarnings("unchecked") public JsonDeserializer buildBeanDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { // First: check what creators we can use, if any ValueInstantiator valueInstantiator; /* 04-Jun-2015, tatu: To work around [databind#636], need to catch the * issue, defer; this seems like a reasonable good place for now. * Note, however, that for non-Bean types (Collections, Maps) this * probably won't work and needs to be added elsewhere. */ try { valueInstantiator = findValueInstantiator(ctxt, beanDesc); } catch (NoClassDefFoundError error) { return new ErrorThrowingDeserializer(error); } catch (IllegalArgumentException e) { // 05-Apr-2017, tatu: Although it might appear cleaner to require collector // to throw proper exception, it doesn't actually have reference to this // instance so... throw InvalidDefinitionException.from(ctxt.getParser(), e.getMessage(), beanDesc, null); } BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc); builder.setValueInstantiator(valueInstantiator); // And then setters for deserializing from JSON Object addBeanProps(ctxt, beanDesc, builder); addObjectIdReader(ctxt, beanDesc, builder); // managed/back reference fields/setters need special handling... first part addBackReferenceProperties(ctxt, beanDesc, builder); addInjectables(ctxt, beanDesc, builder); final DeserializationConfig config = ctxt.getConfig(); if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { builder = mod.updateBuilder(config, beanDesc, builder); } } JsonDeserializer deserializer; if (type.isAbstract() && !valueInstantiator.canInstantiate()) { deserializer = builder.buildAbstract(); } else { deserializer = builder.build(); } // may have modifier(s) that wants to modify or replace serializer we just built // (note that `resolve()` and `createContextual()` called later on) if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { deserializer = mod.modifyDeserializer(config, beanDesc, deserializer); } } return (JsonDeserializer) deserializer; } /** * Method for constructing a bean deserializer that uses specified * intermediate Builder for binding data, and construction of the * value instance. * Note that implementation is mostly copied from the regular * BeanDeserializer build method. */ @SuppressWarnings("unchecked") protected JsonDeserializer buildBuilderBasedDeserializer( DeserializationContext ctxt, JavaType valueType, BeanDescription builderDesc) throws JsonMappingException { // Creators, anyone? (to create builder itself) ValueInstantiator valueInstantiator; try { valueInstantiator = findValueInstantiator(ctxt, builderDesc); } catch (NoClassDefFoundError error) { return new ErrorThrowingDeserializer(error); } catch (IllegalArgumentException e) { // 05-Apr-2017, tatu: Although it might appear cleaner to require collector // to throw proper exception, it doesn't actually have reference to this // instance so... throw InvalidDefinitionException.from(ctxt.getParser(), e.getMessage(), builderDesc, null); } final DeserializationConfig config = ctxt.getConfig(); BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, builderDesc); builder.setValueInstantiator(valueInstantiator); // And then "with methods" for deserializing from JSON Object addBeanProps(ctxt, builderDesc, builder); addObjectIdReader(ctxt, builderDesc, builder); // managed/back reference fields/setters need special handling... first part addBackReferenceProperties(ctxt, builderDesc, builder); addInjectables(ctxt, builderDesc, builder); JsonPOJOBuilder.Value builderConfig = builderDesc.findPOJOBuilderConfig(); final String buildMethodName = (builderConfig == null) ? JsonPOJOBuilder.DEFAULT_BUILD_METHOD : builderConfig.buildMethodName; // and lastly, find build method to use: AnnotatedMethod buildMethod = builderDesc.findMethod(buildMethodName, null); if (buildMethod != null) { // note: can't yet throw error; may be given build method if (config.canOverrideAccessModifiers()) { ClassUtil.checkAndFixAccess(buildMethod.getMember(), config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } } builder.setPOJOBuilder(buildMethod, builderConfig); // this may give us more information... if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { builder = mod.updateBuilder(config, builderDesc, builder); } } JsonDeserializer deserializer = builder.buildBuilderBased( valueType, buildMethodName); // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built: if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { deserializer = mod.modifyDeserializer(config, builderDesc, deserializer); } } return (JsonDeserializer) deserializer; } protected void addObjectIdReader(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder) throws JsonMappingException { ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo(); if (objectIdInfo == null) { return; } Class implClass = objectIdInfo.getGeneratorType(); JavaType idType; SettableBeanProperty idProp; ObjectIdGenerator gen; ObjectIdResolver resolver = ctxt.objectIdResolverInstance(beanDesc.getClassInfo(), objectIdInfo); // Just one special case: Property-based generator is trickier if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work PropertyName propName = objectIdInfo.getPropertyName(); idProp = builder.findProperty(propName); if (idProp == null) { throw new IllegalArgumentException("Invalid Object Id definition for " +beanDesc.getBeanClass().getName()+": cannot find property with name '"+propName+"'"); } idType = idProp.getType(); gen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); } else { JavaType type = ctxt.constructType(implClass); idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; idProp = null; gen = ctxt.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); } // also: unlike with value deserializers, let's just resolve one we need here JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); builder.setObjectIdReader(ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), gen, deser, idProp, resolver)); } @SuppressWarnings("unchecked") public JsonDeserializer buildThrowableDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { final DeserializationConfig config = ctxt.getConfig(); // first: construct like a regular bean deserializer... BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc); builder.setValueInstantiator(findValueInstantiator(ctxt, beanDesc)); addBeanProps(ctxt, beanDesc, builder); // (and assume there won't be any back references) // But then let's decorate things a bit // Need to add "initCause" as setter for exceptions (sub-classes of Throwable). AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS); if (am != null) { // should never be null SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am, new PropertyName("cause")); SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef, am.getParameterType(0)); if (prop != null) { // 21-Aug-2011, tatus: We may actually have found 'cause' property // to set... but let's replace it just in case, otherwise can end up with odd errors. builder.addOrReplaceProperty(prop, true); } } // And also need to ignore "localizedMessage" builder.addIgnorable("localizedMessage"); // Java 7 also added "getSuppressed", skip if we have such data: builder.addIgnorable("suppressed"); // As well as "message": it will be passed via constructor, // as there's no 'setMessage()' method // 23-Jan-2018, tatu: ... although there MAY be Creator Property... which is problematic // builder.addIgnorable("message"); // update builder now that all information is in? if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { builder = mod.updateBuilder(config, beanDesc, builder); } } JsonDeserializer deserializer = builder.build(); /* At this point it ought to be a BeanDeserializer; if not, must assume * it's some other thing that can handle deserialization ok... */ if (deserializer instanceof BeanDeserializer) { deserializer = new ThrowableDeserializer((BeanDeserializer) deserializer); } // may have modifier(s) that wants to modify or replace serializer we just built: if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { deserializer = mod.modifyDeserializer(config, beanDesc, deserializer); } } return (JsonDeserializer) deserializer; } /* /********************************************************** /* Helper methods for Bean deserializer construction, /* overridable by sub-classes /********************************************************** */ /** * Overridable method that constructs a {@link BeanDeserializerBuilder} * which is used to accumulate information needed to create deserializer * instance. */ protected BeanDeserializerBuilder constructBeanDeserializerBuilder(DeserializationContext ctxt, BeanDescription beanDesc) { return new BeanDeserializerBuilder(beanDesc, ctxt); } /** * Method called to figure out settable properties for the * bean deserializer to use. *

* Note: designed to be overridable, and effort is made to keep interface * similar between versions. */ protected void addBeanProps(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder) throws JsonMappingException { final boolean isConcrete = !beanDesc.getType().isAbstract(); final SettableBeanProperty[] creatorProps = isConcrete ? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig()) : null; final boolean hasCreatorProps = (creatorProps != null); // 01-May-2016, tatu: Which base type to use here gets tricky, since // it may often make most sense to use general type for overrides, // but what we have here may be more specific impl type. But for now // just use it as is. JsonIgnoreProperties.Value ignorals = ctxt.getConfig() .getDefaultPropertyIgnorals(beanDesc.getBeanClass(), beanDesc.getClassInfo()); Set ignored; if (ignorals != null) { boolean ignoreAny = ignorals.getIgnoreUnknown(); builder.setIgnoreUnknownProperties(ignoreAny); // Or explicit/implicit definitions? ignored = ignorals.findIgnoredForDeserialization(); for (String propName : ignored) { builder.addIgnorable(propName); } } else { ignored = Collections.emptySet(); } // Also, do we have a fallback "any" setter? AnnotatedMember anySetter = beanDesc.findAnySetterAccessor(); if (anySetter != null) { builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter)); } else { // 23-Jan-2018, tatu: although [databind#1805] would suggest we should block // properties regardless, for now only consider unless there's any setter... Collection ignored2 = beanDesc.getIgnoredPropertyNames(); if (ignored2 != null) { for (String propName : ignored2) { // allow ignoral of similarly named JSON property, but do not force; // latter means NOT adding this to 'ignored': builder.addIgnorable(propName); } } } final boolean useGettersAsSetters = ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS) && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS); // Ok: let's then filter out property definitions List propDefs = filterBeanProps(ctxt, beanDesc, builder, beanDesc.findProperties(), ignored); // After which we can let custom code change the set if (_factoryConfig.hasDeserializerModifiers()) { for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs); } } // At which point we still have all kinds of properties; not all with mutators: for (BeanPropertyDefinition propDef : propDefs) { SettableBeanProperty prop = null; // 18-Oct-2013, tatu: Although constructor parameters have highest precedence, // we need to do linkage (as per [databind#318]), and so need to start with // other types, and only then create constructor parameter, if any. if (propDef.hasSetter()) { AnnotatedMethod setter = propDef.getSetter(); JavaType propertyType = setter.getParameterType(0); prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType); } else if (propDef.hasField()) { AnnotatedField field = propDef.getField(); JavaType propertyType = field.getType(); prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType); } else { // NOTE: specifically getter, since field was already checked above AnnotatedMethod getter = propDef.getGetter(); if (getter != null) { if (useGettersAsSetters && _isSetterlessType(getter.getRawType())) { // 23-Jan-2018, tatu: As per [databind#1805], need to ensure we don't // accidentally sneak in getter-as-setter for `READ_ONLY` properties if (builder.hasIgnorable(propDef.getName())) { ; } else { prop = constructSetterlessProperty(ctxt, beanDesc, propDef); } } else if (!propDef.hasConstructorParameter()) { PropertyMetadata md = propDef.getMetadata(); // 25-Oct-2016, tatu: If merging enabled, might not need setter. // We cannot quite support this with creator parameters; in theory // possibly, but right not not due to complexities of routing, so // just prevent if (md.getMergeInfo() != null) { prop = constructSetterlessProperty(ctxt, beanDesc, propDef); } } } } // 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types // (since they are never used anyway) if (hasCreatorProps && propDef.hasConstructorParameter()) { /* If property is passed via constructor parameter, we must * handle things in special way. Not sure what is the most optimal way... * for now, let's just call a (new) method in builder, which does nothing. */ // but let's call a method just to allow custom builders to be aware... final String name = propDef.getName(); CreatorProperty cprop = null; if (creatorProps != null) { for (SettableBeanProperty cp : creatorProps) { if (name.equals(cp.getName()) && (cp instanceof CreatorProperty)) { cprop = (CreatorProperty) cp; break; } } } if (cprop == null) { List n = new ArrayList<>(); for (SettableBeanProperty cp : creatorProps) { n.add(cp.getName()); } ctxt.reportBadPropertyDefinition(beanDesc, propDef, "Could not find creator property with name '%s' (known Creator properties: %s)", name, n); continue; } if (prop != null) { cprop.setFallbackSetter(prop); } Class[] views = propDef.findViews(); if (views == null) { views = beanDesc.findDefaultViews(); } cprop.setViews(views); builder.addCreatorProperty(cprop); continue; } if (prop != null) { // one more thing before adding to builder: copy any metadata Class[] views = propDef.findViews(); if (views == null) { views = beanDesc.findDefaultViews(); } prop.setViews(views); builder.addProperty(prop); } } } private boolean _isSetterlessType(Class rawType) { // May also need to consider getters // for Map/Collection properties; but with lowest precedence // should only consider Collections and Maps, for now? return Collection.class.isAssignableFrom(rawType) || Map.class.isAssignableFrom(rawType); } /** * Helper method called to filter out explicit ignored properties, * as well as properties that have "ignorable types". * Note that this will not remove properties that have no * setters. */ protected List filterBeanProps(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder, List propDefsIn, Set ignored) throws JsonMappingException { ArrayList result = new ArrayList( Math.max(4, propDefsIn.size())); HashMap,Boolean> ignoredTypes = new HashMap,Boolean>(); // These are all valid setters, but we do need to introspect bit more for (BeanPropertyDefinition property : propDefsIn) { String name = property.getName(); if (ignored.contains(name)) { // explicit ignoral using @JsonIgnoreProperties needs to block entries continue; } if (!property.hasConstructorParameter()) { // never skip constructor params Class rawPropertyType = property.getRawPrimaryType(); // Some types are declared as ignorable as well if ((rawPropertyType != null) && isIgnorableType(ctxt.getConfig(), property, rawPropertyType, ignoredTypes)) { // important: make ignorable, to avoid errors if value is actually seen builder.addIgnorable(name); continue; } } result.add(property); } return result; } /** * Method that will find if bean has any managed- or back-reference properties, * and if so add them to bean, to be linked during resolution phase. * * @since 2.9 */ protected void addBackReferenceProperties(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder) throws JsonMappingException { // and then back references, not necessarily found as regular properties List refProps = beanDesc.findBackReferences(); if (refProps != null) { for (BeanPropertyDefinition refProp : refProps) { /* AnnotatedMember m = refProp.getMutator(); JavaType type; if (m instanceof AnnotatedMethod) { type = ((AnnotatedMethod) m).getParameterType(0); } else { type = m.getType(); // 30-Mar-2017, tatu: Unfortunately it is not yet possible to make back-refs // work through constructors; but let's at least indicate the issue for now if (m instanceof AnnotatedParameter) { ctxt.reportBadTypeDefinition(beanDesc, "Cannot bind back reference using Creator parameter (reference '%s', parameter index #%d)", name, ((AnnotatedParameter) m).getIndex()); } } */ String refName = refProp.findReferenceName(); builder.addBackReferenceProperty(refName, constructSettableProperty(ctxt, beanDesc, refProp, refProp.getPrimaryType())); } } } @Deprecated // since 2.9 (rename) protected void addReferenceProperties(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder) throws JsonMappingException { addBackReferenceProperties(ctxt, beanDesc, builder); } /** * Method called locate all members used for value injection (if any), * constructor {@link com.fasterxml.jackson.databind.deser.impl.ValueInjector} instances, and add them to builder. */ protected void addInjectables(DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder) throws JsonMappingException { Map raw = beanDesc.findInjectables(); if (raw != null) { for (Map.Entry entry : raw.entrySet()) { AnnotatedMember m = entry.getValue(); builder.addInjectable(PropertyName.construct(m.getName()), m.getType(), beanDesc.getClassAnnotations(), m, entry.getKey()); } } } /** * Method called to construct fallback {@link SettableAnyProperty} * for handling unknown bean properties, given a method that * has been designated as such setter. * * @param mutator Either 2-argument method (setter, with key and value), or Field * that contains Map; either way accessor used for passing "any values" */ @SuppressWarnings("unchecked") protected SettableAnyProperty constructAnySetter(DeserializationContext ctxt, BeanDescription beanDesc, AnnotatedMember mutator) throws JsonMappingException { //find the java type based on the annotated setter method or setter field BeanProperty prop; JavaType keyType; JavaType valueType; if (mutator instanceof AnnotatedMethod) { // we know it's a 2-arg method, second arg is the value AnnotatedMethod am = (AnnotatedMethod) mutator; keyType = am.getParameterType(0); valueType = am.getParameterType(1); valueType = resolveMemberAndTypeAnnotations(ctxt, mutator, valueType); prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()), valueType, null, mutator, PropertyMetadata.STD_OPTIONAL); } else if (mutator instanceof AnnotatedField) { AnnotatedField af = (AnnotatedField) mutator; // get the type from the content type of the map object JavaType mapType = af.getType(); mapType = resolveMemberAndTypeAnnotations(ctxt, mutator, mapType); keyType = mapType.getKeyType(); valueType = mapType.getContentType(); prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()), mapType, null, mutator, PropertyMetadata.STD_OPTIONAL); } else { return ctxt.reportBadDefinition(beanDesc.getType(), String.format( "Unrecognized mutator type for any setter: %s", mutator.getClass())); } // First: see if there are explicitly specified // and then possible direct deserializer override on accessor KeyDeserializer keyDeser = findKeyDeserializerFromAnnotation(ctxt, mutator); if (keyDeser == null) { keyDeser = keyType.getValueHandler(); } if (keyDeser == null) { keyDeser = ctxt.findKeyDeserializer(keyType, prop); } else { if (keyDeser instanceof ContextualKeyDeserializer) { keyDeser = ((ContextualKeyDeserializer) keyDeser) .createContextual(ctxt, prop); } } JsonDeserializer deser = findContentDeserializerFromAnnotation(ctxt, mutator); if (deser == null) { deser = valueType.getValueHandler(); } if (deser != null) { // As per [databind#462] need to ensure we contextualize deserializer before passing it on deser = (JsonDeserializer) ctxt.handlePrimaryContextualization(deser, prop, valueType); } TypeDeserializer typeDeser = valueType.getTypeHandler(); return new SettableAnyProperty(prop, mutator, valueType, keyDeser, deser, typeDeser); } /** * Method that will construct a regular bean property setter using * the given setter method. * * @return Property constructed, if any; or null to indicate that * there should be no property based on given definitions. */ protected SettableBeanProperty constructSettableProperty(DeserializationContext ctxt, BeanDescription beanDesc, BeanPropertyDefinition propDef, JavaType propType0) throws JsonMappingException { // need to ensure method is callable (for non-public) AnnotatedMember mutator = propDef.getNonConstructorMutator(); // 08-Sep-2016, tatu: issues like [databind#1342] suggest something fishy // going on; add sanity checks to try to pin down actual problem... // Possibly passing creator parameter? if (mutator == null) { ctxt.reportBadPropertyDefinition(beanDesc, propDef, "No non-constructor mutator available"); } JavaType type = resolveMemberAndTypeAnnotations(ctxt, mutator, propType0); // Does the Method specify the deserializer to use? If so, let's use it. TypeDeserializer typeDeser = type.getTypeHandler(); SettableBeanProperty prop; if (mutator instanceof AnnotatedMethod) { prop = new MethodProperty(propDef, type, typeDeser, beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator); } else { // 08-Sep-2016, tatu: wonder if we should verify it is `AnnotatedField` to be safe? prop = new FieldProperty(propDef, type, typeDeser, beanDesc.getClassAnnotations(), (AnnotatedField) mutator); } JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, mutator); if (deser == null) { deser = type.getValueHandler(); } if (deser != null) { deser = ctxt.handlePrimaryContextualization(deser, prop, type); prop = prop.withValueDeserializer(deser); } // need to retain name of managed forward references: AnnotationIntrospector.ReferenceProperty ref = propDef.findReferenceType(); if (ref != null && ref.isManagedReference()) { prop.setManagedReferenceName(ref.getName()); } ObjectIdInfo objectIdInfo = propDef.findObjectIdInfo(); if (objectIdInfo != null){ prop.setObjectIdInfo(objectIdInfo); } return prop; } /** * Method that will construct a regular bean property setter using * the given setter method. */ protected SettableBeanProperty constructSetterlessProperty(DeserializationContext ctxt, BeanDescription beanDesc, BeanPropertyDefinition propDef) throws JsonMappingException { final AnnotatedMethod getter = propDef.getGetter(); JavaType type = resolveMemberAndTypeAnnotations(ctxt, getter, getter.getType()); TypeDeserializer typeDeser = type.getTypeHandler(); SettableBeanProperty prop = new SetterlessProperty(propDef, type, typeDeser, beanDesc.getClassAnnotations(), getter); JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, getter); if (deser == null) { deser = type.getValueHandler(); } if (deser != null) { deser = ctxt.handlePrimaryContextualization(deser, prop, type); prop = prop.withValueDeserializer(deser); } return prop; } /* /********************************************************** /* Helper methods for Bean deserializer, other /********************************************************** */ /** * Helper method used to skip processing for types that we know * cannot be (i.e. are never consider to be) beans: * things like primitives, Arrays, Enums, and proxy types. *

* Note that usually we shouldn't really be getting these sort of * types anyway; but better safe than sorry. */ protected boolean isPotentialBeanType(Class type) { String typeStr = ClassUtil.canBeABeanType(type); if (typeStr != null) { throw new IllegalArgumentException("Cannot deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean"); } if (ClassUtil.isProxyType(type)) { throw new IllegalArgumentException("Cannot deserialize Proxy class "+type.getName()+" as a Bean"); } /* also: can't deserialize some local classes: static are ok; in-method not; * other non-static inner classes are ok */ typeStr = ClassUtil.isLocalType(type, true); if (typeStr != null) { throw new IllegalArgumentException("Cannot deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean"); } return true; } /** * Helper method that will check whether given raw type is marked as always ignorable * (for purpose of ignoring properties with type) */ protected boolean isIgnorableType(DeserializationConfig config, BeanPropertyDefinition propDef, Class type, Map,Boolean> ignoredTypes) { Boolean status = ignoredTypes.get(type); if (status != null) { return status.booleanValue(); } // 22-Oct-2016, tatu: Slight check to skip primitives, String if ((type == String.class) || type.isPrimitive()) { status = Boolean.FALSE; } else { // 21-Apr-2016, tatu: For 2.8, can specify config overrides status = config.getConfigOverride(type).getIsIgnoredType(); if (status == null) { BeanDescription desc = config.introspectClassAnnotations(type); status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo()); // We default to 'false', i.e. not ignorable if (status == null) { status = Boolean.FALSE; } } } ignoredTypes.put(type, status); return status.booleanValue(); } /** * @since 2.8.11 */ protected void _validateSubType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { SubTypeValidator.instance().validateSubType(ctxt, type, beanDesc); } }