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.node.ObjectNode;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
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.
*/
@SuppressWarnings("unchecked")
@Override
public JsonDeserializer createBeanDeserializer(DeserializationContext ctxt,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
// First: we may also have custom overrides:
JsonDeserializer> deser = _findCustomBeanDeserializer(type, config, beanDesc);
if (deser != null) {
// [databind#2392]
if (_factoryConfig.hasDeserializerModifiers()) {
for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
deser = mod.modifyDeserializer(ctxt.getConfig(), beanDesc, deser);
}
}
return (JsonDeserializer) deser;
}
// 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:
deser = findStdDeserializer(ctxt, type, beanDesc);
if (deser != null) {
return (JsonDeserializer)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);
// 05-May-2020, tatu: [databind#2683] Let's actually pre-emptively catch
// certain types (for now, java.time.*) to give better error messages
deser = _findUnsupportedTypeDeserializer(ctxt, type, beanDesc);
if (deser != null) {
return (JsonDeserializer)deser;
}
// Use generic bean introspection to build deserializer
return buildBeanDeserializer(ctxt, type, beanDesc);
}
@Override
public JsonDeserializer createBuilderBasedDeserializer(
DeserializationContext ctxt, JavaType valueType, BeanDescription valueBeanDesc,
Class> builderClass)
throws JsonMappingException
{
// First: need a BeanDescription for builder class
JavaType builderType;
if (ctxt.isEnabled(MapperFeature.INFER_BUILDER_TYPE_BINDINGS)) {
builderType = ctxt.getTypeFactory().constructParametricType(builderClass, valueType.getBindings());
} else {
builderType = ctxt.constructType(builderClass);
}
BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType, valueBeanDesc);
// 20-Aug-2020, tatu: May want to change at some point (after 2.12) to pass "valueBeanDesc"
// too; no urgent need at this point
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;
}
/**
* Helper method called to see if given type, otherwise to be taken as POJO type,
* is "known but not supported" JDK type, and if so, return alternate handler
* (deserializer).
* Initially added to support more meaningful error messages when "Java 8 date/time"
* support module not registered.
*
* @since 2.12
*/
protected JsonDeserializer _findUnsupportedTypeDeserializer(DeserializationContext ctxt,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
// 05-May-2020, tatu: Should we check for possible Shape override to "POJO"?
// (to let users force 'serialize-as-POJO'? Or not?
final String errorMsg = BeanUtil.checkUnsupportedType(type);
if (errorMsg != null) {
// 30-Sep-2020, tatu: [databind#2867] Avoid checks if there is a mix-in
// which likely providers a handler...
if (ctxt.getConfig().findMixInClassFor(type.getRawClass()) == null) {
return new UnsupportedTypeDeserializer(type, errorMsg);
}
}
return null;
}
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 e0) {
// 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(),
ClassUtil.exceptionMessage(e0),
beanDesc, null)
.withCause(e0);
}
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(),
ClassUtil.exceptionMessage(e),
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(String.format(
"Invalid Object Id definition for %s: cannot find property with name %s",
ClassUtil.getTypeDescription(beanDesc.getType()),
ClassUtil.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).
// 26-May-2022, tatu: [databind#3275] Looks like JDK 12 added "setCause()"
// which can wreak havoc, at least with NamingStrategy
Iterator it = builder.getProperties();
while (it.hasNext()) {
SettableBeanProperty prop = it.next();
if ("setCause".equals(prop.getMember().getName())) {
// For now this is allowed as we are returned "live" Iterator...
it.remove();
break;
}
}
AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS);
if (am != null) { // should never be null
// [databind#3497]: must consider possible PropertyNamingStrategy
String name = "cause";
PropertyNamingStrategy pts = config.getPropertyNamingStrategy();
if (pts != null) {
name = pts.nameForSetterMethod(config, am, "cause");
}
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am,
new PropertyName(name));
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);
}
}
// 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 = ThrowableDeserializer.construct(ctxt, (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 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();
}
JsonIncludeProperties.Value inclusions = ctxt.getConfig()
.getDefaultPropertyInclusions(beanDesc.getBeanClass(),
beanDesc.getClassInfo());
Set included = null;
if (inclusions != null) {
included = inclusions.getIncluded();
if (included != null) {
for(String propName : included) {
builder.addIncludable(propName);
}
}
}
// 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, included);
// 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;
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)",
ClassUtil.name(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.
*
* @deprecated in 2.12, remove from 3.0
*/
@Deprecated
protected List filterBeanProps(DeserializationContext ctxt,
BeanDescription beanDesc, BeanDeserializerBuilder builder,
List propDefsIn,
Set ignored)
throws JsonMappingException
{
return filterBeanProps(ctxt, beanDesc, builder, propDefsIn, ignored, null);
}
/**
* 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.
*
* @since 2.12
*/
protected List filterBeanProps(DeserializationContext ctxt,
BeanDescription beanDesc, BeanDeserializerBuilder builder,
List propDefsIn,
Set ignored,
Set included)
{
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();
// explicit ignoral using @JsonIgnoreProperties of @JsonIncludeProperties needs to block entries
if (IgnorePropertiesUtil.shouldIgnore(name, ignored, included)) {
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)",
ClassUtil.name(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;
final boolean isField = mutator instanceof AnnotatedField;
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 (isField) {
AnnotatedField af = (AnnotatedField) mutator;
// get the type from the content type of the map object
JavaType fieldType = af.getType();
// 31-Jul-2022, tatu: Not just Maps any more but also JsonNode, so:
if (fieldType.isMapLikeType()) {
fieldType = resolveMemberAndTypeAnnotations(ctxt, mutator, fieldType);
keyType = fieldType.getKeyType();
valueType = fieldType.getContentType();
prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
fieldType, null, mutator, PropertyMetadata.STD_OPTIONAL);
} else if (fieldType.hasRawClass(JsonNode.class)
|| fieldType.hasRawClass(ObjectNode.class)) {
fieldType = resolveMemberAndTypeAnnotations(ctxt, mutator, fieldType);
// Deserialize is individual values of ObjectNode, not full ObjectNode, so:
valueType = ctxt.constructType(JsonNode.class);
prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
fieldType, null, mutator, PropertyMetadata.STD_OPTIONAL);
// Unlike with more complicated types, here we do not allow any annotation
// overrides etc but instead short-cut handling:
return SettableAnyProperty.constructForJsonNodeField(ctxt,
prop, mutator, valueType,
ctxt.findRootValueDeserializer(valueType));
} else {
return ctxt.reportBadDefinition(beanDesc.getType(), String.format(
"Unsupported type for any-setter: %s -- only support `Map`s, `JsonNode` and `ObjectNode` ",
ClassUtil.getTypeDescription(fieldType)));
}
} else {
return ctxt.reportBadDefinition(beanDesc.getType(), String.format(
"Unrecognized mutator type for any-setter: %s",
ClassUtil.nameOf(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();
if (isField) {
return SettableAnyProperty.constructForMapField(ctxt,
prop, mutator, valueType, keyDeser, deser, typeDeser);
}
return SettableAnyProperty.constructForMethod(ctxt,
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);
}
}