DeserializationContext.java
package tools.jackson.databind;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import tools.jackson.core.*;
import tools.jackson.core.tree.ArrayTreeNode;
import tools.jackson.core.tree.ObjectTreeNode;
import tools.jackson.core.type.ResolvedType;
import tools.jackson.core.type.TypeReference;
import tools.jackson.core.util.JacksonFeatureSet;
import tools.jackson.databind.cfg.*;
import tools.jackson.databind.deser.*;
import tools.jackson.databind.deser.impl.ObjectIdReader;
import tools.jackson.databind.deser.impl.TypeWrappedDeserializer;
import tools.jackson.databind.exc.*;
import tools.jackson.databind.introspect.*;
import tools.jackson.databind.jsontype.TypeDeserializer;
import tools.jackson.databind.jsontype.TypeIdResolver;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.TreeTraversingParser;
import tools.jackson.databind.type.LogicalType;
import tools.jackson.databind.type.TypeFactory;
import tools.jackson.databind.util.*;
/**
* Context for the process of deserialization a single root-level value.
* Used to allow passing in configuration settings and reusable temporary
* objects (scrap arrays, containers).
* Constructed by {@link ObjectMapper} (and {@link ObjectReader} based on
* configuration,
* used mostly by {@link ValueDeserializer}s to access contextual information.
*/
public abstract class DeserializationContext
extends DatabindContext
implements ObjectReadContext // 3.0
{
/*
/**********************************************************************
/* Per-mapper configuration (immutable via ObjectReader)
/**********************************************************************
*/
/**
* Low-level {@link TokenStreamFactory} that may be used for constructing
* embedded parsers.
*/
final protected TokenStreamFactory _streamFactory;
/**
* Read-only factory instance; exposed to let
* owners (<code>ObjectMapper</code>, <code>ObjectReader</code>)
* access it.
*/
final protected DeserializerFactory _factory;
/**
* Object that handle details of {@link ValueDeserializer} caching.
*/
final protected DeserializerCache _cache;
/*
/**********************************************************************
/* Configuration that may vary by ObjectReader
/**********************************************************************
*/
/**
* Generic deserialization processing configuration
*/
final protected DeserializationConfig _config;
/**
* Bitmap of {@link DeserializationFeature}s that are enabled
*/
final protected int _featureFlags;
/**
* Currently active view, if any.
*/
final protected Class<?> _activeView;
/**
* Schema for underlying parser to use, if any.
*/
final protected FormatSchema _schema;
/**
* Object used for resolving references to injectable
* values.
*/
final protected InjectableValues _injectableValues;
/*
/**********************************************************************
/* Other State
/**********************************************************************
*/
/**
* Currently active parser used for deserialization.
* May be different from the outermost parser
* when content is buffered.
*/
protected transient JsonParser _parser;
/**
* Capabilities of the input format.
*/
protected transient JacksonFeatureSet<StreamReadCapability> _readCapabilities;
/*
/**********************************************************************
/* Per-operation reusable helper objects (not for blueprints)
/**********************************************************************
*/
protected transient ArrayBuilders _arrayBuilders;
protected transient ObjectBuffer _objectBuffer;
protected transient DateFormat _dateFormat;
/**
* Lazily-constructed holder for per-call attributes.
*/
protected transient ContextAttributes _attributes;
/**
* Type of {@link ValueDeserializer} on which {@link ValueDeserializer#createContextual}
* is being called currently.
*/
protected LinkedNode<JavaType> _currentType;
/**
* Lazily constructed {@link ClassIntrospector} instance: created from "blueprint"
*/
protected transient ClassIntrospector _classIntrospector;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
protected DeserializationContext(TokenStreamFactory streamFactory,
DeserializerFactory df, DeserializerCache cache,
DeserializationConfig config, FormatSchema schema,
InjectableValues injectableValues)
{
_streamFactory = streamFactory;
_factory = Objects.requireNonNull(df, "Cannot pass null DeserializerFactory");
_cache = cache;
_config = config;
_featureFlags = config.getDeserializationFeatures();
_activeView = config.getActiveView();
_schema = schema;
_injectableValues = injectableValues;
_attributes = config.getAttributes();
}
/*
/**********************************************************************
/* DatabindContext implementation
/**********************************************************************
*/
@Override
public DeserializationConfig getConfig() { return _config; }
@Override
public final Class<?> getActiveView() { return _activeView; }
@Override
public final boolean canOverrideAccessModifiers() {
return _config.canOverrideAccessModifiers();
}
@Override
public final boolean isEnabled(MapperFeature feature) {
return _config.isEnabled(feature);
}
@Override
public final boolean isEnabled(DatatypeFeature feature) {
return _config.isEnabled(feature);
}
@Override
public final DatatypeFeatures getDatatypeFeatures() {
return _config.getDatatypeFeatures();
}
@Override
public final JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType) {
return _config.getDefaultPropertyFormat(baseType);
}
@Override
public final AnnotationIntrospector getAnnotationIntrospector() {
return _config.getAnnotationIntrospector();
}
@Override
public final TypeFactory getTypeFactory() {
return _config.getTypeFactory();
}
@Override // since 2.11
public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
throws IllegalArgumentException
{
if (baseType.hasRawClass(subclass)) {
return baseType;
}
// On deserialization side, still uses "strict" type-compatibility checking;
// see [databind#2632] about serialization side
return getConfig().getTypeFactory().constructSpecializedType(baseType, subclass, false);
}
/**
* Method for accessing default Locale to use: convenience method for
*<pre>
* getConfig().getLocale();
*</pre>
*/
@Override
public Locale getLocale() {
return _config.getLocale();
}
/**
* Method for accessing default TimeZone to use: convenience method for
*<pre>
* getConfig().getTimeZone();
*</pre>
*/
@Override
public TimeZone getTimeZone() {
return _config.getTimeZone();
}
/*
/**********************************************************************
/* Access to per-call state, like generic attributes
/**********************************************************************
*/
@Override
public Object getAttribute(Object key) {
return _attributes.getAttribute(key);
}
@Override
public DeserializationContext setAttribute(Object key, Object value)
{
_attributes = _attributes.withPerCallAttribute(key, value);
return this;
}
/**
* Accessor to {@link JavaType} of currently contextualized
* {@link ValueDeserializer}, if any.
* This is sometimes useful for generic {@link ValueDeserializer}s that
* do not get passed (or do not retain) type information when being
* constructed: happens for example for deserializers constructed
* from annotations.
*
* @return Type of {@link ValueDeserializer} being contextualized,
* if process is on-going; null if not.
*/
public JavaType getContextualType() {
return (_currentType == null) ? null : _currentType.value();
}
/*
/**********************************************************************
/* ObjectReadContext impl, config access
/**********************************************************************
*/
@Override
public TokenStreamFactory tokenStreamFactory() {
return _streamFactory;
}
@Override
public FormatSchema getSchema() {
return _schema;
}
@Override
public StreamReadConstraints streamReadConstraints() {
return _streamFactory.streamReadConstraints();
}
@Override
public int getStreamReadFeatures(int defaults) {
return _config.getStreamReadFeatures();
}
@Override
public int getFormatReadFeatures(int defaults) {
return _config.getFormatReadFeatures();
}
/*
/**********************************************************************
/* ObjectReadContext impl, Tree creation
/**********************************************************************
*/
@Override
public ArrayTreeNode createArrayNode() {
return getNodeFactory().arrayNode();
}
@Override
public ObjectTreeNode createObjectNode() {
return getNodeFactory().objectNode();
}
/*
/**********************************************************************
/* ObjectReadContext impl, databind
/**********************************************************************
*/
@SuppressWarnings("unchecked")
@Override
public JsonNode readTree(JsonParser p) throws JacksonException
{
// NOTE: inlined version of `_bindAsTree()` from `ObjectReader`
JsonToken t = p.currentToken();
if (t == null) {
t = p.nextToken();
if (t == null) { // [databind#1406]: expose end-of-input as `null`
return null;
}
}
if (t == JsonToken.VALUE_NULL) {
return getNodeFactory().nullNode();
}
ValueDeserializer<Object> deser = findRootValueDeserializer(ObjectReader.JSON_NODE_TYPE);
return (JsonNode) deser.deserialize(p, this);
}
/**
* Convenience method that may be used by composite or container deserializers,
* for reading one-off values contained (for sequences, it is more efficient
* to actually fetch deserializer once for the whole collection).
*<p>
* NOTE: when deserializing values of properties contained in composite types,
* rather use {@link #readPropertyValue(JsonParser, BeanProperty, Class)};
* this method does not allow use of contextual annotations.
*/
@Override
public <T> T readValue(JsonParser p, Class<T> type) throws JacksonException {
return readValue(p, getTypeFactory().constructType(type));
}
@Override
public <T> T readValue(JsonParser p, TypeReference<T> refType) throws JacksonException {
return readValue(p, getTypeFactory().constructType(refType));
}
@Override
public <T> T readValue(JsonParser p, ResolvedType type) throws JacksonException {
if (type instanceof JavaType jt) {
return readValue(p, jt);
}
throw new UnsupportedOperationException(
"Only support `JavaType` implementation of `ResolvedType`, not: "+type.getClass().getName());
}
@SuppressWarnings("unchecked")
public <T> T readValue(JsonParser p, JavaType type) throws JacksonException {
ValueDeserializer<Object> deser = findRootValueDeserializer(type);
if (deser == null) {
reportBadDefinition(type,
"Could not find `ValueDeserializer` for type "+ClassUtil.getTypeDescription(type));
}
return (T) _readValue(p, deser);
}
/**
* Helper method that should handle special cases for deserialization; most
* notably handling {@code null} (and possibly absent values).
*/
private Object _readValue(JsonParser p, ValueDeserializer<Object> deser) throws JacksonException
{
// 12-Oct-2025, tatu: As per [databind#5344], need to ensure parser points to token
if (!p.hasCurrentToken()) {
p.nextToken();
}
if (p.hasToken(JsonToken.VALUE_NULL)) {
return deser.getNullValue(this);
}
return deser.deserialize(p, this);
}
/*
/**********************************************************************
/* Public API, config feature accessors
/**********************************************************************
*/
/**
* Convenience method for checking whether specified on/off
* feature is enabled
*/
public final boolean isEnabled(DeserializationFeature feat) {
// 03-Dec-2010, tatu: minor shortcut; since this is called quite often,
// let's use a local copy of feature settings:
return (_featureFlags & feat.getMask()) != 0;
}
/**
* Bulk access method for getting the bit mask of all {@link DeserializationFeature}s
* that are enabled.
*/
public final int getDeserializationFeatures() {
return _featureFlags;
}
/**
* Bulk access method for checking that all features specified by
* mask are enabled.
*/
public final boolean hasDeserializationFeatures(int featureMask) {
return (_featureFlags & featureMask) == featureMask;
}
/**
* Bulk access method for checking that at least one of features specified by
* mask is enabled.
*/
public final boolean hasSomeOfFeatures(int featureMask) {
return (_featureFlags & featureMask) != 0;
}
/**
* Accessor for checking whether input format has specified capability
* or not.
*
* @return True if input format has specified capability; false if not
*/
public final boolean isEnabled(StreamReadCapability cap) {
return _readCapabilities.isEnabled(cap);
}
/*
/**********************************************************************
/* Public API, accessor for helper objects
/**********************************************************************
*/
/**
* Method for accessing the currently active parser.
* May be different from the outermost parser
* when content is buffered.
*<p>
* Use of this method is discouraged: if code has direct access
* to the active parser, that should be used instead.
*/
public final JsonParser getParser() { return _parser; }
public final Object findInjectableValue(Object valueId,
BeanProperty forProperty, Object beanInstance, Boolean optional, Boolean useInput)
{
InjectableValues injectables = _injectableValues;
if (injectables == null) {
injectables = InjectableValues.empty();
}
return injectables.findInjectableValue(this, valueId, forProperty, beanInstance,
optional, useInput);
}
/**
* Convenience method for accessing the default Base64 encoding
* used for decoding base64 encoded binary content.
* Same as calling:
*<pre>
* getConfig().getBase64Variant();
*</pre>
*/
public final Base64Variant getBase64Variant() {
return _config.getBase64Variant();
}
/**
* Convenience method, functionally equivalent to:
*<pre>
* getConfig().getNodeFactory();
* </pre>
*/
public final JsonNodeFactory getNodeFactory() {
return _config.getNodeFactory();
}
/*
/**********************************************************************
/* Annotation, BeanDescription introspection
/**********************************************************************
*/
@Override
protected ClassIntrospector classIntrospector() {
if (_classIntrospector == null) {
_classIntrospector = _config.classIntrospectorInstance();
}
return _classIntrospector;
}
@Override
public BeanDescription introspectBeanDescription(JavaType type, AnnotatedClass ac) {
return classIntrospector().introspectForDeserialization(type, ac);
}
public BeanDescription introspectBeanDescriptionForCreation(JavaType type) {
return introspectBeanDescriptionForCreation(type,
classIntrospector().introspectClassAnnotations(type));
}
public BeanDescription introspectBeanDescriptionForCreation(JavaType type, AnnotatedClass ac) {
return classIntrospector().introspectForCreation(type, ac);
}
public BeanDescription.Supplier lazyIntrospectBeanDescriptionForCreation(JavaType type) {
return new BeanDescription.LazySupplier(getConfig(), type) {
@Override
protected BeanDescription _construct(JavaType forType, AnnotatedClass ac) {
return introspectBeanDescriptionForCreation(forType, ac);
}
@Override
protected AnnotatedClass _introspect(JavaType forType) {
return introspectClassAnnotations(forType);
}
};
}
public BeanDescription introspectBeanDescriptionForBuilder(JavaType builderType,
BeanDescription valueTypeDesc) {
return classIntrospector().introspectForDeserializationWithBuilder(builderType,
valueTypeDesc);
}
public BeanDescription.Supplier lazyIntrospectBeanDescriptionForBuilder(final JavaType builderType,
final BeanDescription valueTypeDesc) {
return new BeanDescription.LazySupplier(getConfig(), builderType) {
@Override
protected BeanDescription _construct(JavaType forType, AnnotatedClass ac) {
return introspectBeanDescriptionForBuilder(forType, valueTypeDesc);
}
@Override
protected AnnotatedClass _introspect(JavaType forType) {
return introspectClassAnnotations(forType);
}
};
}
/*
/**********************************************************************
/* Misc config access
/**********************************************************************
*/
@Override
public PropertyName findRootName(JavaType rootType) {
return _config.findRootName(this, rootType);
}
@Override
public PropertyName findRootName(Class<?> rawRootType) {
return _config.findRootName(this, rawRootType);
}
/**
* Method that can be used to see whether there is an explicitly registered deserializer
* for given type: this is true for supported JDK types, as well as third-party types
* for which {@code Module} provides support but is NOT true (that is, returns {@code false})
* for POJO types for which {@code BeanDeserializer} is generated based on discovered
* properties.
*<p>
* Note that it is up to {@code Module}s to implement support for this method: some
* do (like basic {@code SimpleModule}).
*
* @param valueType Type-erased type to check
*
* @return True if this factory has explicit (non-POJO) deserializer for specified type,
* or has a provider (of type {@link Deserializers}) that has.
*/
public boolean hasExplicitDeserializerFor(Class<?> valueType) {
return _factory.hasExplicitDeserializerFor(this, valueType);
}
/*
/**********************************************************************
/* Public API, CoercionConfig access
/**********************************************************************
*/
/**
* General-purpose accessor for finding what to do when specified coercion
* from shape that is now always allowed to be coerced from is requested.
*
* @param targetType Logical target type of coercion
* @param targetClass Physical target type of coercion
* @param inputShape Input shape to coerce from
*
* @return CoercionAction configured for specific coercion
*/
public CoercionAction findCoercionAction(LogicalType targetType,
Class<?> targetClass, CoercionInputShape inputShape)
{
return _config.findCoercionAction(targetType, targetClass, inputShape);
}
/**
* More specialized accessor called in case of input being a blank
* String (one consisting of only white space characters with length of at least one).
* Will basically first determine if "blank as empty" is allowed: if not,
* returns {@code actionIfBlankNotAllowed}, otherwise returns action for
* {@link CoercionInputShape#EmptyString}.
*
* @param targetType Logical target type of coercion
* @param targetClass Physical target type of coercion
* @param actionIfBlankNotAllowed Return value to use in case "blanks as empty"
* is not allowed
*
* @return CoercionAction configured for specified coercion from blank string
*/
public CoercionAction findCoercionFromBlankString(LogicalType targetType,
Class<?> targetClass,
CoercionAction actionIfBlankNotAllowed)
{
return _config.findCoercionFromBlankString(targetType, targetClass, actionIfBlankNotAllowed);
}
/*
/**********************************************************************
/* Factory methods for getting appropriate TokenBuffer instances
/* (possibly overridden by backends for alternate data formats)
/**********************************************************************
*/
/**
* Factory method used for creating {@link TokenBuffer} to temporarily
* contain copy of content read from specified parser; usually for purpose
* of reading contents later on (possibly augmeneted with injected additional
* content)
*/
public TokenBuffer bufferForInputBuffering(JsonParser p) {
return TokenBuffer.forBuffering(p, this);
}
/**
* Convenience method that is equivalent to:
*<pre>
* ctxt.bufferForInputBuffering(ctxt.getParser());
*</pre>
*/
public final TokenBuffer bufferForInputBuffering() {
return bufferForInputBuffering(getParser());
}
/**
* Convenience method, equivalent to:
*<pre>
* TokenBuffer buffer = ctxt.bufferForInputBuffering(parser);
* buffer.copyCurrentStructure(parser);
* return buffer;
*</pre>
*<p>
* NOTE: the whole "current value" that parser points to is read and
* buffered, including Object and Array values (if parser pointing to
* start marker).
*/
public TokenBuffer bufferAsCopyOfValue(JsonParser p) throws JacksonException
{
TokenBuffer buf = bufferForInputBuffering(p);
buf.copyCurrentStructure(p);
return buf;
}
/*
/**********************************************************************
/* Public API, value deserializer access
/**********************************************************************
*/
/**
* Method for finding a value deserializer, and creating a contextual
* version if necessary, for value reached via specified property.
*/
@SuppressWarnings("unchecked")
public final ValueDeserializer<Object> findContextualValueDeserializer(JavaType type,
BeanProperty prop)
{
ValueDeserializer<Object> deser = _cache.findValueDeserializer(this, _factory, type);
if (deser != null) {
deser = (ValueDeserializer<Object>) handleSecondaryContextualization(deser, prop, type);
}
return deser;
}
/**
* Variant that will try to locate deserializer for current type, but without
* performing any contextualization (unlike {@link #findContextualValueDeserializer})
* or checking for need to create a {@link TypeDeserializer} (unlike
* {@link #findRootValueDeserializer(JavaType)}.
* This method is usually called from within {@link ValueDeserializer#resolve},
* and expectation is that caller then calls either
* {@link #handlePrimaryContextualization(ValueDeserializer, BeanProperty, JavaType)} or
* {@link #handleSecondaryContextualization(ValueDeserializer, BeanProperty, JavaType)} at a
* later point, as necessary.
*/
public final ValueDeserializer<Object> findNonContextualValueDeserializer(JavaType type)
{
return _cache.findValueDeserializer(this, _factory, type);
}
/**
* Method for finding a deserializer for root-level value.
*/
@SuppressWarnings("unchecked")
public final ValueDeserializer<Object> findRootValueDeserializer(JavaType type)
{
ValueDeserializer<Object> deser = _cache.findValueDeserializer(this,
_factory, type);
if (deser == null) { // can this occur?
return null;
}
deser = (ValueDeserializer<Object>) handleSecondaryContextualization(deser, null, type);
TypeDeserializer typeDeser = findTypeDeserializer(type);
if (typeDeser != null) {
// important: contextualize to indicate this is for root value
typeDeser = typeDeser.forProperty(null);
return new TypeWrappedDeserializer(typeDeser, deser);
}
return deser;
}
/*
/**********************************************************************
/* Public API, (value) type deserializer access
/**********************************************************************
*/
/**
* Method called to find and create a type information deserializer for given base type,
* if one is needed. If not needed (no polymorphic handling configured for type),
* should return null.
*<p>
* Note that this method is usually only directly called for values of container (Collection,
* array, Map) types and root values, but not for bean property values.
*
* @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 findTypeDeserializer(JavaType baseType)
{
return findTypeDeserializer(baseType, introspectClassAnnotations(baseType));
}
public TypeDeserializer findTypeDeserializer(JavaType baseType,
AnnotatedClass classAnnotations)
{
try {
return _config.getTypeResolverProvider().findTypeDeserializer(this,
baseType, classAnnotations);
} catch (IllegalArgumentException | IllegalStateException e) {
throw InvalidDefinitionException.from(getParser(),
ClassUtil.exceptionMessage(e), baseType)
.withCause(e);
}
}
/**
* 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.
*
* @since 3.0
*/
public TypeDeserializer findPropertyTypeDeserializer(JavaType baseType,
AnnotatedMember accessor)
{
try {
return _config.getTypeResolverProvider().findPropertyTypeDeserializer(this,
accessor, baseType);
} catch (IllegalArgumentException | IllegalStateException e) {
throw InvalidDefinitionException.from(getParser(),
ClassUtil.exceptionMessage(e), baseType)
.withCause(e);
}
}
/**
* 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 accessor Field or method that contains container property
*
* @since 3.0
*/
public TypeDeserializer findPropertyContentTypeDeserializer(JavaType containerType,
AnnotatedMember accessor)
{
try {
return _config.getTypeResolverProvider().findPropertyContentTypeDeserializer(this,
accessor, containerType);
} catch (IllegalArgumentException | IllegalStateException e) {
throw InvalidDefinitionException.from(getParser(),
ClassUtil.exceptionMessage(e), containerType)
.withCause(e);
}
}
/*
/**********************************************************************
/* Public API, key deserializer access
/**********************************************************************
*/
public final KeyDeserializer findKeyDeserializer(JavaType keyType,
BeanProperty prop)
{
KeyDeserializer kd;
// 15-Jun-2021, tatu: Needed wrt [databind#3143]
try {
kd = _cache.findKeyDeserializer(this, _factory, keyType);
} catch (IllegalArgumentException iae) {
// We better only expose checked exceptions, since those
// are what caller is expected to handle
reportBadDefinition(keyType, ClassUtil.exceptionMessage(iae));
kd = null;
}
// Second: contextualize?
if (kd instanceof ContextualKeyDeserializer ckd) {
kd = ckd.createContextual(this, prop);
}
return kd;
}
/**
* Method that will drop all dynamically constructed deserializers (ones that
* are counted as result value for {@link DeserializerCache#cachedDeserializersCount}).
* This can be used to remove memory usage (in case some deserializers are
* only used once or so), or to force re-construction of deserializers after
* configuration changes for mapper than owns the provider.
* @since 2.19
*/
public void flushCachedDeserializers() {
_cache.flushCachedDeserializers();
}
/*
/**********************************************************************
/* Public API, ObjectId handling
/**********************************************************************
*/
/**
* Method called to find and return entry corresponding to given
* Object Id: will add an entry if necessary, and never returns null
*/
public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator, ObjectIdResolver resolver);
/**
* Method called to ensure that every object id encounter during processing
* are resolved.
*
* @throws UnresolvedForwardReference
*/
public abstract void checkUnresolvedObjectId()
throws UnresolvedForwardReference;
/*
/**********************************************************************
/* Public API, type handling
/**********************************************************************
*/
/**
* Convenience method, functionally equivalent to:
*<pre>
* getConfig().constructType(cls);
* </pre>
*/
public final JavaType constructType(Class<?> cls) {
return (cls == null) ? null : _config.constructType(cls);
}
/**
* Helper method that is to be used when resolving basic class name into
* Class instance, the reason being that it may be necessary to work around
* various ClassLoader limitations, as well as to handle primitive type
* signatures.
*/
public Class<?> findClass(String className) throws ClassNotFoundException
{
// By default, delegate to ClassUtil: can be overridden with custom handling
return getTypeFactory().findClass(className);
}
/*
/**********************************************************************
/* Public API, helper object recycling
/**********************************************************************
*/
/**
* Method that can be used to get access to a reusable ObjectBuffer,
* useful for efficiently constructing Object arrays and Lists.
* Note that leased buffers should be returned once deserializer
* is done, to allow for reuse during same round of deserialization.
*/
public final ObjectBuffer leaseObjectBuffer()
{
ObjectBuffer buf = _objectBuffer;
if (buf == null) {
buf = new ObjectBuffer();
} else {
_objectBuffer = null;
}
return buf;
}
/**
* Method to call to return object buffer previously leased with
* {@link #leaseObjectBuffer}.
*
* @param buf Returned object buffer
*/
public final void returnObjectBuffer(ObjectBuffer buf)
{
// Already have a reusable buffer? Let's retain bigger one
// (or if equal, favor newer one, shorter life-cycle)
if (_objectBuffer == null
|| buf.initialCapacity() >= _objectBuffer.initialCapacity()) {
_objectBuffer = buf;
}
}
/**
* Method for accessing object useful for building arrays of
* primitive types (such as int[]).
*/
public final ArrayBuilders getArrayBuilders()
{
if (_arrayBuilders == null) {
_arrayBuilders = new ArrayBuilders();
}
return _arrayBuilders;
}
/*
/**********************************************************************
/* Extended API: handler instantiation
/**********************************************************************
*/
public abstract ValueDeserializer<Object> deserializerInstance(Annotated annotated,
Object deserDef);
public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated,
Object deserDef);
/*
/**********************************************************************
/* Extended API: resolving contextual deserializers; called
/* by structured deserializers for their value/component deserializers
/**********************************************************************
*/
/**
* Method called for primary property deserializers (ones
* directly created to deserialize values of a POJO property),
* to handle details of calling
* {@link ValueDeserializer#createContextual} with given property context.
*
* @param prop Property for which the given primary deserializer is used; never null.
*/
public ValueDeserializer<?> handlePrimaryContextualization(ValueDeserializer<?> deser,
BeanProperty prop, JavaType type)
{
if (deser != null) {
_currentType = new LinkedNode<JavaType>(type, _currentType);
try {
deser = deser.createContextual(this, prop);
} finally {
_currentType = _currentType.next();
}
}
return deser;
}
/**
* Method called for secondary property deserializers (ones
* NOT directly created to deal with an annotatable POJO property,
* but instead created as a component -- such as value deserializers
* for structured types, or deserializers for root values)
* to handle details of resolving
* {@link ValueDeserializer#createContextual} with given property context.
* Given that these deserializers are not directly related to given property
* (or, in case of root value property, to any property), annotations
* accessible may or may not be relevant.
*
* @param prop Property for which deserializer is used, if any; null
* when deserializing root values
*/
public ValueDeserializer<?> handleSecondaryContextualization(ValueDeserializer<?> deser,
BeanProperty prop, JavaType type)
{
if (deser != null) {
_currentType = new LinkedNode<JavaType>(type, _currentType);
try {
deser =deser.createContextual(this, prop);
} finally {
_currentType = _currentType.next();
}
}
return deser;
}
/*
/**********************************************************************
/* Parsing methods that may use reusable/-cyclable objects
/**********************************************************************
*/
/**
* Convenience method for parsing a Date from given String, using
* currently configured date format (accessed using
* {@link DeserializationConfig#getDateFormat()}).
*<p>
* Implementation will handle thread-safety issues related to
* date formats such that first time this method is called,
* date format is cloned, and cloned instance will be retained
* for use during this deserialization round.
*/
public Date parseDate(String dateStr) throws IllegalArgumentException
{
try {
DateFormat df = _getDateFormat();
return df.parse(dateStr);
} catch (ParseException e) {
throw new IllegalArgumentException(String.format(
"Failed to parse Date value '%s': %s", dateStr,
ClassUtil.exceptionMessage(e)));
}
}
/**
* Convenience method for constructing Calendar instance set
* to specified time, to be modified and used by caller.
*/
public Calendar constructCalendar(Date d) {
// 08-Jan-2008, tatu: not optimal, but should work for the most part; let's revise as needed.
Calendar c = Calendar.getInstance(getTimeZone());
c.setTime(d);
return c;
}
/*
/**********************************************************************
/* Extension points for more esoteric data coercion
/**********************************************************************
*/
/**
* Method to call in case incoming shape is Object Value (and parser thereby
* points to {@link tools.jackson.core.JsonToken#START_OBJECT} token),
* but a Scalar value (potentially coercible from String value) is expected.
* This would typically be used to deserialize a Number, Boolean value or some other
* "simple" unstructured value type.
*<p>
* Note that expected behavior in case of extraction not succeeding changed in
* Jackson 2.20: now {@code null} is expected to be returned in that case (in 2.19
* and before, exception was to be thrown, but this prevented fallback handling
* via {@link DeserializationProblemHandler#handleUnexpectedToken}.
*
* @param p Actual parser to read content from
* @param deser Deserializer that needs extracted String value
* @param scalarType Immediate type of scalar to extract; usually type deserializer
* handles but not always (for example, deserializer for {@code int[]} would pass
* scalar type of {@code int})
*
* @return String value found, if any; {@code null} if none (note: changed in 2.20;
* before that in 2.19 and before exception throwing was expected)
*
* @throws JacksonException If there are problems either reading content (underlying parser
* problem) or finding expected scalar value
*/
public String extractScalarFromObject(JsonParser p, ValueDeserializer<?> deser,
Class<?> scalarType)
throws JacksonException
{
// 17-May-2025, tatu: [databind#4656] must NOT call `handleUnexpectedToken`
// since that can return value other than {@code String}
// return (String) handleUnexpectedToken(constructType(scalarType), p);
return null;
}
/*
/**********************************************************************
/* Convenience methods for reading parsed values
/**********************************************************************
*/
/**
* Convenience method that may be used by composite or container deserializers,
* for reading one-off values for the composite type, taking into account
* annotations that the property (passed to this method -- usually property that
* has custom serializer that called this method) has.
*
* @param p Parser that points to the first token of the value to read
* @param prop Logical property of a POJO being type
* @return Value of type {@code type} that was read
*/
public <T> T readPropertyValue(JsonParser p, BeanProperty prop, Class<T> type)
throws JacksonException
{
return readPropertyValue(p, prop, getTypeFactory().constructType(type));
}
/**
* Same as {@link #readPropertyValue(JsonParser, BeanProperty, Class)} but with
* fully resolved {@link JavaType} as target: needs to be used for generic types,
* for example.
*/
@SuppressWarnings("unchecked")
public <T> T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type)
throws JacksonException
{
ValueDeserializer<Object> deser = findContextualValueDeserializer(type, prop);
if (deser == null) {
return reportBadDefinition(type, String.format(
"Could not find `ValueDeserializer` for type %s (via property %s)",
ClassUtil.getTypeDescription(type), ClassUtil.nameOf(prop)));
}
return (T) _readValue(p, deser);
}
/**
* Helper method similar to {@link ObjectReader#treeToValue(TreeNode, Class)}
* which will read contents of given tree ({@link JsonNode})
* and bind them into specified target type. This is often used in two-phase
* deserialization in which content is first read as a tree, then manipulated
* (adding and/or removing properties of Object values, for example),
* and finally converted into actual target type using default deserialization
* logic for the type.
*<p>
* NOTE: deserializer implementations should be careful not to try to recursively
* deserialize into target type deserializer has registered itself to handle.
*
* @param n Tree value to convert, if not {@code null}: if {@code null}, will simply
* return {@code null}
* @param targetType Type to deserialize contents of {@code n} into (if {@code n} not {@code null})
*
* @return Either {@code null} (if {@code n} was {@code null} or a value of
* type {@code type} that was read from non-{@code null} {@code n} argument
*/
public <T> T readTreeAsValue(JsonNode n, Class<T> targetType)
throws JacksonException
{
if (n == null || n.isMissingNode()) {
return null;
}
try (TreeTraversingParser p = _treeAsTokens(n)) {
return readValue(p, targetType);
}
}
/**
* Same as {@link #readTreeAsValue(JsonNode, Class)} but will fully resolved
* {@link JavaType} as {@code targetType}
*<p>
* NOTE: deserializer implementations should be careful not to try to recursively
* deserialize into target type deserializer has registered itself to handle.
*
* @param n Tree value to convert
* @param targetType Type to deserialize contents of {@code n} into
*
* @return Value of type {@code type} that was read
*/
public <T> T readTreeAsValue(JsonNode n, JavaType targetType)
throws JacksonException
{
if (n == null || n.isMissingNode()) {
return null;
}
try (TreeTraversingParser p = _treeAsTokens(n)) {
return readValue(p, targetType);
}
}
private TreeTraversingParser _treeAsTokens(JsonNode n)
{
TreeTraversingParser p = new TreeTraversingParser(n, this);
// important: must initialize...
p.nextToken();
return p;
}
/*
/**********************************************************************
/* Methods for problem handling
/**********************************************************************
*/
/**
* Method that deserializers should call if they encounter an unrecognized
* property (and once that is not explicitly designed as ignorable), to
* inform possibly configured {@link DeserializationProblemHandler}s and
* let it handle the problem.
*
* @return True if there was a configured problem handler that was able to handle the
* problem
*/
public boolean handleUnknownProperty(JsonParser p, ValueDeserializer<?> deser,
Object instanceOrClass, String propName)
throws JacksonException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
if (h.value().handleUnknownProperty(this, p, deser, instanceOrClass, propName)) {
return true;
}
h = h.next();
}
// Nope, not handled. Potentially that's a problem...
if (!isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
p.skipChildren();
return true;
}
// Do we know properties that are expected instead?
Collection<Object> propIds = (deser == null) ? null : deser.getKnownPropertyNames();
throw UnrecognizedPropertyException.from(_parser,
instanceOrClass, propName, propIds);
}
/**
* Method that deserializers should call if they encounter a String value
* that cannot be converted to expected key of a {@link java.util.Map}
* valued property.
* Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will throw {@link InvalidFormatException} with given message.
*
* @param keyClass Expected type for key
* @param keyValue String value from which to deserialize key
* @param msg Error message template caller wants to use if exception is to be thrown
* @param msgArgs Optional arguments to use for message, if any
*
* @return Key value to use
*
* @throws JacksonException To indicate unrecoverable problem, usually based on <code>msg</code>
*/
public Object handleWeirdKey(Class<?> keyClass, String keyValue,
String msg, Object... msgArgs)
throws JacksonException
{
// but if not handled, just throw exception
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
Object key = h.value().handleWeirdKey(this, keyClass, keyValue, msg);
if (key != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if ((key == null) || keyClass.isInstance(key)) {
return key;
}
throw weirdStringException(keyValue, keyClass, String.format(
"DeserializationProblemHandler.handleWeirdKey() for type %s returned value of type %s",
ClassUtil.getClassDescription(keyClass),
ClassUtil.getClassDescription(key)
));
}
h = h.next();
}
throw weirdKeyException(keyClass, keyValue, msg);
}
/**
* Method that deserializers should call if they encounter a String value
* that cannot be converted to target property type, in cases where some
* String values could be acceptable (either with different settings,
* or different value).
* Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdStringValue}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will throw {@link InvalidFormatException} with given message.
*
* @param targetClass Type of property into which incoming String should be converted
* @param value String value from which to deserialize property value
* @param msg Error message template caller wants to use if exception is to be thrown
* @param msgArgs Optional arguments to use for message, if any
*
* @return Property value to use
*
* @throws JacksonException To indicate unrecoverable problem, usually based on <code>msg</code>
*/
public Object handleWeirdStringValue(Class<?> targetClass, String value,
String msg, Object... msgArgs)
throws JacksonException
{
// but if not handled, just throw exception
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
Object instance = h.value().handleWeirdStringValue(this, targetClass, value, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if (_isCompatible(targetClass, instance)) {
return instance;
}
throw weirdStringException(value, targetClass, String.format(
"`DeserializationProblemHandler.handleWeirdStringValue()` for type %s returned value of type %s",
ClassUtil.getClassDescription(targetClass),
ClassUtil.getClassDescription(instance)
));
}
h = h.next();
}
throw weirdStringException(value, targetClass, msg);
}
/**
* Method that deserializers should call if they encounter a numeric value
* that cannot be converted to target property type, in cases where some
* numeric values could be acceptable (either with different settings,
* or different numeric value).
* Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will throw {@link InvalidFormatException} with given message.
*
* @param targetClass Type of property into which incoming number should be converted
* @param value Number value from which to deserialize property value
* @param msg Error message template caller wants to use if exception is to be thrown
* @param msgArgs Optional arguments to use for message, if any
*
* @return Property value to use
*
* @throws JacksonException To indicate unrecoverable problem, usually based on <code>msg</code>
*/
public Object handleWeirdNumberValue(Class<?> targetClass, Number value,
String msg, Object... msgArgs)
throws JacksonException
{
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
Object key = h.value().handleWeirdNumberValue(this, targetClass, value, msg);
if (key != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if (_isCompatible(targetClass, key)) {
return key;
}
throw weirdNumberException(value, targetClass, _format(
"`DeserializationProblemHandler.handleWeirdNumberValue()` for type %s returned value of type %s",
ClassUtil.getClassDescription(targetClass),
ClassUtil.getClassDescription(key)
));
}
h = h.next();
}
throw weirdNumberException(value, targetClass, msg);
}
public Object handleWeirdNativeValue(JavaType targetType, Object badValue,
JsonParser p)
throws JacksonException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
final Class<?> raw = targetType.getRawClass();
for (; h != null; h = h.next()) {
// Can bail out if it's handled
Object goodValue = h.value().handleWeirdNativeValue(this, targetType, badValue, p);
if (goodValue != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if ((goodValue == null) || raw.isInstance(goodValue)) {
return goodValue;
}
throw DatabindException.from(p, _format(
"`DeserializationProblemHandler.handleWeirdNativeValue()` for type %s returned value of type %s",
ClassUtil.getClassDescription(targetType),
ClassUtil.getClassDescription(goodValue)
));
}
}
throw weirdNativeValueException(badValue, raw);
}
/**
* Method that deserializers should call if they encounter a null value and
* target value type is a Primitive type.
* Default implementation will try to call {@link DeserializationProblemHandler#handleNullForPrimitives}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will call {@link #reportInputMismatch} with given message,
* which will throw {@link MismatchedInputException}.
*
* @param targetClass Primitive type into which incoming {@code null} value should be converted to
* @param p Parser that points to the {@code null} read
* @param deser Type of {@link ValueDeserializer} calling this method
* @param msgTemplate Error message template caller wants to use if exception is to be thrown
* @param msgArgs Arguments for {@code msgTemplate} (if any)
*
* @throws JacksonException To indicate unrecoverable problem, usually based on <code>msg</code>
*
* @since 3.1
*/
public Object handleNullForPrimitives(Class<?> targetClass,
JsonParser p, ValueDeserializer<?> deser,
String msgTemplate, Object... msgArgs)
throws JacksonException
{
// but if not handled, just throw exception
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
String msg = _format(msgTemplate, msgArgs);
while (h != null) {
// Can bail out if it's handled
Object instance = h.value().handleNullForPrimitives(this, targetClass, p, deser, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if (_isCompatible(targetClass, instance)) {
return instance;
}
// In case our problem handler providing incompatible value,
throw new InvalidFormatException(_parser,
String.format("`DeserializationProblemHandler.handleNullForPrimitives()` for type %s returned value of type %s",
ClassUtil.nameOf(targetClass), ClassUtil.getClassDescription(instance)),
instance, targetClass
);
}
h = h.next();
}
return reportInputMismatch(deser, msg);
}
/**
* Method that deserializers should call if they fail to instantiate value
* due to lack of viable instantiator (usually creator, that is, constructor
* or static factory method). Method should be called at point where value
* has not been decoded, so that handler has a chance to handle decoding
* using alternate mechanism, and handle underlying content (possibly by
* just skipping it) to keep input state valid
*
* @param instClass Type that was to be instantiated
* @param valueInst (optional) Value instantiator to be used, if any; null if type does not
* use one for instantiation (custom deserialiers don't; standard POJO deserializer does)
* @param p Parser that points to the input value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*/
@SuppressWarnings("resource")
public Object handleMissingInstantiator(Class<?> instClass, ValueInstantiator valueInst,
JsonParser p, String msg, Object... msgArgs)
throws JacksonException
{
if (p == null) {
p = getParser();
}
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
Object instance = h.value().handleMissingInstantiator(this,
instClass, valueInst, p, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if (_isCompatible(instClass, instance)) {
return instance;
}
reportBadDefinition(constructType(instClass), String.format(
"`DeserializationProblemHandler.handleMissingInstantiator()` for type %s returned value of type %s",
ClassUtil.getClassDescription(instClass),
ClassUtil.getClassDescription((instance)
)));
}
h = h.next();
}
// 16-Oct-2016, tatu: This is either a definition problem (if no applicable creator
// exists), or input mismatch problem (otherwise) since none of existing creators
// match with token.
// 24-Oct-2019, tatu: Further, as per [databind#2522], passing `null` ValueInstantiator
// should simply trigger definition problem
if (valueInst == null ) {
msg = String.format("Cannot construct instance of %s: %s",
ClassUtil.nameOf(instClass), msg);
return reportBadDefinition(instClass, msg);
}
if (!valueInst.canInstantiate()) {
msg = String.format("Cannot construct instance of %s (no Creators, like default constructor, exist): %s",
ClassUtil.nameOf(instClass), msg);
return reportBadDefinition(instClass, msg);
}
msg = String.format("Cannot construct instance of %s (although at least one Creator exists): %s",
ClassUtil.nameOf(instClass), msg);
return reportInputMismatch(instClass, msg);
}
/**
* Method that deserializers should call if they fail to instantiate value
* due to an exception that was thrown by constructor (or other mechanism used
* to create instances).
* Default implementation will try to call {@link DeserializationProblemHandler#handleInstantiationProblem}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will throw exception constructed with {@link #instantiationException}.
*
* @param instClass Type that was to be instantiated
* @param argument (optional) Argument that was passed to constructor or equivalent
* instantiator; often a {@link java.lang.String}.
* @param t Exception that caused failure
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*/
public Object handleInstantiationProblem(Class<?> instClass, Object argument,
Throwable t)
throws JacksonException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
Object instance = h.value().handleInstantiationProblem(this, instClass, argument, t);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if (_isCompatible(instClass, instance)) {
return instance;
}
reportBadDefinition(constructType(instClass), String.format(
"DeserializationProblemHandler.handleInstantiationProblem() for type %s returned value of type %s",
ClassUtil.getClassDescription(instClass),
ClassUtil.classNameOf(instance)
));
}
h = h.next();
}
// 18-May-2016, tatu: Only wrap if not already a valid type to throw
ClassUtil.throwIfJacksonE(t);
// [databind#2164]: but see if wrapping is desired
if (!isEnabled(DeserializationFeature.WRAP_EXCEPTIONS)) {
ClassUtil.throwIfRTE(t);
}
throw instantiationException(instClass, t);
}
// 15-Sep-2019, tatu: Remove from 3.0 due to [databind#2133] adding `JavaType` overloads
/*
public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
JsonParser p, String msg, Object... msgArgs)
throws JacksonException
{
return handleUnexpectedToken(constructType(instClass), t, p, msg, msgArgs);
}
*/
public Object handleUnexpectedToken(Class<?> instClass, JsonParser p)
throws JacksonException
{
return handleUnexpectedToken(constructType(instClass), p.currentToken(), p, null);
}
/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* cannot handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param targetType Type that was to be instantiated
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*/
public Object handleUnexpectedToken(JavaType targetType, JsonParser p)
throws JacksonException
{
return handleUnexpectedToken(targetType, p.currentToken(), p, null);
}
/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* cannot handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param targetType Type that was to be instantiated
* @param t Token encountered that does not match expected
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*/
public Object handleUnexpectedToken(JavaType targetType, JsonToken t,
JsonParser p, String msg, Object... msgArgs)
throws JacksonException
{
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
Object instance = h.value().handleUnexpectedToken(this,
targetType, t, p, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
if (_isCompatible(targetType.getRawClass(), instance)) {
return instance;
}
reportBadDefinition(targetType, String.format(
"DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
ClassUtil.getTypeDescription(targetType),
ClassUtil.classNameOf(instance)
));
}
h = h.next();
}
if (msg == null) {
final String targetDesc = ClassUtil.getTypeDescription(targetType);
if (t == null) {
msg = String.format("Unexpected end-of-input when trying read value of type %s",
targetDesc);
} else {
msg = String.format("Cannot deserialize value of type %s from %s (token `JsonToken.%s`)",
targetDesc, _shapeForToken(t), t);
}
}
// 18-Jun-2020, tatu: to resolve [databind#2770], force access to `getText()` for scalars
if ((t != null) && t.isScalarValue()) {
p.getString();
}
reportInputMismatch(targetType, msg);
return null; // never gets here
}
/**
* Method that deserializers should call if they encounter a type id
* (for polymorphic deserialization) that cannot be resolved to an
* actual type; usually since there is no mapping defined.
* Default implementation will try to call {@link DeserializationProblemHandler#handleUnknownTypeId}
* on configured handlers, if any, to allow for recovery; if recovery does not
* succeed, will throw exception constructed with {@link #invalidTypeIdException}.
*
* @param baseType Base type from which resolution starts
* @param id Type id that could not be converted
* @param extraDesc Additional problem description to add to default exception message,
* if resolution fails.
*
* @return {@link JavaType} that id resolves to
*
* @throws JacksonException To indicate unrecoverable problem, if resolution cannot
* be made to work
*/
public JavaType handleUnknownTypeId(JavaType baseType, String id,
TypeIdResolver idResolver, String extraDesc) throws JacksonException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
JavaType type = h.value().handleUnknownTypeId(this, baseType, id, idResolver, extraDesc);
if (type != null) {
if (type.hasRawClass(Void.class)) {
return null;
}
// But ensure there's type compatibility
if (type.isTypeOrSubTypeOf(baseType.getRawClass())) {
return type;
}
throw invalidTypeIdException(baseType, id,
"problem handler tried to resolve into non-subtype: "+
ClassUtil.getTypeDescription(type));
}
h = h.next();
}
// 24-May-2016, tatu: Actually we may still not want to fail quite yet
if (!isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
return null;
}
throw invalidTypeIdException(baseType, id, extraDesc);
}
public JavaType handleMissingTypeId(JavaType baseType,
TypeIdResolver idResolver, String extraDesc) throws JacksonException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
// Can bail out if it's handled
JavaType type = h.value().handleMissingTypeId(this, baseType, idResolver, extraDesc);
if (type != null) {
if (type.hasRawClass(Void.class)) {
return null;
}
// But ensure there's type compatibility
if (type.isTypeOrSubTypeOf(baseType.getRawClass())) {
return type;
}
throw invalidTypeIdException(baseType, null,
"problem handler tried to resolve into non-subtype: "+
ClassUtil.getTypeDescription(type));
}
h = h.next();
}
// 09-Mar-2017, tatu: We may want to consider yet another feature at some
// point to allow returning `null`... but that seems bit risky for now
// if (!isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
// return null;
// }
throw missingTypeIdException(baseType, extraDesc);
}
/**
* Method that deserializer may call if it is called to do an update ("merge")
* but deserializer operates on a non-mergeable type. Although this should
* usually be caught earlier, sometimes it may only be caught during operation
* and if so this is the method to call.
* Note that if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled,
* this method will simply return null; otherwise {@link InvalidDefinitionException}
* will be thrown.
*/
public void handleBadMerge(ValueDeserializer<?> deser) throws DatabindException
{
if (!isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
JavaType type = constructType(deser.handledType());
String msg = String.format("Invalid configuration: values of type %s cannot be merged",
ClassUtil.getTypeDescription(type));
throw InvalidDefinitionException.from(getParser(), msg, type);
}
}
protected boolean _isCompatible(Class<?> target, Object value)
{
if ((value == null) || target.isInstance(value)) {
return true;
}
// [databind#1767]: Make sure to allow wrappers for primitive fields
return target.isPrimitive()
&& ClassUtil.wrapperType(target).isInstance(value);
}
/*
/**********************************************************************
/* Methods for problem reporting, in cases where recovery
/* is not considered possible: input problem
/**********************************************************************
*/
/**
* Method for deserializers to call
* when the token encountered was of type different than what <b>should</b>
* be seen at that position, usually within a sequence of expected tokens.
* Note that this method will throw a {@link DatabindException} and no
* recovery is attempted (via {@link DeserializationProblemHandler}, as
* problem is considered to be difficult to recover from, in general.
*/
public void reportWrongTokenException(ValueDeserializer<?> deser,
JsonToken expToken, String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw wrongTokenException(getParser(), deser.handledType(), expToken, msg);
}
/**
* Method for deserializers to call
* when the token encountered was of type different than what <b>should</b>
* be seen at that position, usually within a sequence of expected tokens.
* Note that this method will throw a {@link DatabindException} and no
* recovery is attempted (via {@link DeserializationProblemHandler}, as
* problem is considered to be difficult to recover from, in general.
*/
public void reportWrongTokenException(JavaType targetType,
JsonToken expToken, String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw wrongTokenException(getParser(), targetType, expToken, msg);
}
/**
* Method for deserializers to call
* when the token encountered was of type different than what <b>should</b>
* be seen at that position, usually within a sequence of expected tokens.
* Note that this method will throw a {@link DatabindException} and no
* recovery is attempted (via {@link DeserializationProblemHandler}, as
* problem is considered to be difficult to recover from, in general.
*/
public void reportWrongTokenException(Class<?> targetType,
JsonToken expToken, String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw wrongTokenException(getParser(), targetType, expToken, msg);
}
public <T> T reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean)
throws DatabindException
{
String msg = String.format("No Object Id found for an instance of %s, to assign to property '%s'",
ClassUtil.classNameOf(bean), oidReader.propertyName);
return reportInputMismatch(oidReader.idProperty, msg);
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportInputMismatch(ValueDeserializer<?> src,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw MismatchedInputException.from(getParser(), src.handledType(), msg);
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportInputMismatch(Class<?> targetType,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw MismatchedInputException.from(getParser(), targetType, msg);
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportInputMismatch(JavaType targetType,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
throw MismatchedInputException.from(getParser(), targetType, msg);
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportInputMismatch(BeanProperty prop,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
JavaType type = (prop == null) ? null : prop.getType();
final MismatchedInputException e = MismatchedInputException.from(getParser(), type, msg);
// [databind#2357]: Include property name, if we have it
if (prop != null) {
AnnotatedMember member = prop.getMember();
if (member != null) {
e.prependPath(member.getDeclaringClass(), prop.getName());
}
}
throw e;
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportPropertyInputMismatch(Class<?> targetType, String propertyName,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
MismatchedInputException e = MismatchedInputException.from(getParser(), targetType, msg);
if (propertyName != null) {
e.prependPath(targetType, propertyName);
}
throw e;
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*/
public <T> T reportPropertyInputMismatch(JavaType targetType, String propertyName,
String msg, Object... msgArgs)
throws DatabindException
{
return reportPropertyInputMismatch(targetType.getRawClass(), propertyName, msg, msgArgs);
}
/**
* Helper method used to indicate a problem with input in cases where specific
* input coercion was not allowed.
*/
public <T> T reportBadCoercion(ValueDeserializer<?> src,
Class<?> targetType, Object inputValue,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
InvalidFormatException e = InvalidFormatException.from(getParser(),
msg, inputValue, targetType);
throw e;
}
public <T> T reportTrailingTokens(Class<?> targetType,
JsonParser p, JsonToken trailingToken)
throws DatabindException
{
// 05-Mar-2025, tatu: [databind#5001] Handle non-blocking case separately to give better message
if (trailingToken == JsonToken.NOT_AVAILABLE) {
throw MismatchedInputException.from(p, targetType,
("Incomplete content (`JsonToken.NOT_AVAILABLE`) after value (bound as %s):"
+" not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`"
+" (missing call to `InputFeeder.endOfInput()`?)")
.formatted(ClassUtil.nameOf(targetType)));
}
throw MismatchedInputException.from(p, targetType,
"Trailing token (`JsonToken.%s`) found after value (bound as %s): not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`"
.formatted(trailingToken, ClassUtil.nameOf(targetType)));
}
/*
/**********************************************************************
/* Methods for problem reporting, in cases where recovery
/* is not considered possible: POJO definition problems
/**********************************************************************
*/
/**
* Helper method called to indicate problem in POJO (serialization) definitions or settings
* regarding specific Java type, unrelated to actual JSON content to map.
* Default behavior is to construct and throw a {@link DatabindException}.
*/
@Override
public <T> T reportBadTypeDefinition(BeanDescription bean,
String msg, Object... msgArgs) throws DatabindException
{
String beanDesc = ClassUtil.nameOf(bean.getBeanClass());
msg = String.format("Invalid type definition for type %s: %s", beanDesc, _format(msg, msgArgs));
throw InvalidDefinitionException.from(_parser, msg, bean, null);
}
/**
* Helper method called to indicate problem in POJO (serialization) definitions or settings
* regarding specific property (of a type), unrelated to actual JSON content to map.
* Default behavior is to construct and throw a {@link DatabindException}.
*/
public <T> T reportBadPropertyDefinition(BeanDescription.Supplier beanDescRef,
BeanPropertyDefinition prop, String msg, Object... msgArgs)
throws DatabindException
{
return reportBadPropertyDefinition(beanDescRef.get(), prop, msg, msgArgs);
}
public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
String msg, Object... msgArgs)
throws DatabindException
{
msg = _format(msg, msgArgs);
String propName = ClassUtil.nameOf(prop);
String beanDesc = ClassUtil.nameOf(bean.getBeanClass());
msg = String.format("Invalid definition for property %s (of type %s): %s",
propName, beanDesc, msg);
throw InvalidDefinitionException.from(_parser, msg, bean, prop);
}
@Override
public <T> T reportBadDefinition(JavaType type, String msg)
throws DatabindException
{
throw InvalidDefinitionException.from(_parser, msg, type);
}
/*
/**********************************************************************
/* Methods for constructing semantic exceptions; usually not
/* to be called directly, call `handleXxx()` instead
/**********************************************************************
*/
/**
* Helper method for constructing {@link DatabindException} to indicate
* that the token encountered was of type different than what <b>should</b>
* be seen at that position, usually within a sequence of expected tokens.
* Note that most of the time this method should NOT be directly called;
* instead, {@link #reportWrongTokenException} should be called and will
* call this method as necessary.
*/
public DatabindException wrongTokenException(JsonParser p, JavaType targetType,
JsonToken expToken, String extra)
{
String msg = String.format("Unexpected token (`JsonToken.%s`), expected `JsonToken.%s`",
p.currentToken(), expToken);
msg = _colonConcat(msg, extra);
return MismatchedInputException.from(p, targetType, msg);
}
public DatabindException wrongTokenException(JsonParser p, Class<?> targetType,
JsonToken expToken, String extra)
{
JsonToken t = (p == null) ? null : p.currentToken();
String msg = String.format("Unexpected token (`JsonToken.%s`), expected `JsonToken.%s`", t, expToken);
msg = _colonConcat(msg, extra);
return MismatchedInputException.from(p, targetType, msg);
}
/**
* Helper method for constructing exception to indicate that given JSON
* Object field name was not in format to be able to deserialize specified
* key type.
* Note that most of the time this method should NOT be called; instead,
* {@link #handleWeirdKey} should be called which will call this method
* if necessary.
*/
public DatabindException weirdKeyException(Class<?> keyClass, String keyValue,
String msg) {
return InvalidFormatException.from(_parser,
String.format("Cannot deserialize Map key of type %s from String %s: %s",
ClassUtil.nameOf(keyClass), _quotedString(keyValue), msg),
keyValue, keyClass);
}
/**
* Helper method for constructing exception to indicate that input JSON
* String was not suitable for deserializing into given target type.
* Note that most of the time this method should NOT be called; instead,
* {@link #handleWeirdStringValue} should be called which will call this method
* if necessary.
*
* @param value String value from input being deserialized
* @param instClass Type that String should be deserialized into
* @param msgBase Message that describes specific problem
*/
public DatabindException weirdStringException(String value, Class<?> instClass,
String msgBase)
{
final String msg = String.format("Cannot deserialize value of type %s from String %s: %s",
ClassUtil.nameOf(instClass), _quotedString(value), msgBase);
return InvalidFormatException.from(_parser, msg, value, instClass);
}
/**
* Helper method for constructing exception to indicate that input JSON
* Number was not suitable for deserializing into given target type.
* Note that most of the time this method should NOT be called; instead,
* {@link #handleWeirdNumberValue} should be called which will call this method
* if necessary.
*/
public DatabindException weirdNumberException(Number value, Class<?> instClass,
String msg) {
return InvalidFormatException.from(_parser,
String.format("Cannot deserialize value of type %s from number %s: %s",
ClassUtil.nameOf(instClass), String.valueOf(value), msg),
value, instClass);
}
/**
* Helper method for constructing exception to indicate that input JSON
* token of type "native value" (see {@link JsonToken#VALUE_EMBEDDED_OBJECT})
* is of incompatible type (and there is no delegating creator or such to use)
* and cannot be used to construct value of specified type (usually POJO).
* Note that most of the time this method should NOT be called; instead,
* {@link #handleWeirdNativeValue} should be called which will call this method
*/
public DatabindException weirdNativeValueException(Object value, Class<?> instClass)
{
return InvalidFormatException.from(_parser, String.format(
"Cannot deserialize value of type %s from native value (`JsonToken.VALUE_EMBEDDED_OBJECT`) of type %s: incompatible types",
ClassUtil.nameOf(instClass), ClassUtil.classNameOf(value)),
value, instClass);
}
/**
* Helper method for constructing instantiation exception for specified type,
* to indicate problem with physically constructing instance of
* specified class (missing constructor, exception from constructor)
*<p>
* Note that most of the time this method should NOT be called directly; instead,
* {@link #handleInstantiationProblem} should be called which will call this method
* if necessary.
*/
public DatabindException instantiationException(Class<?> instClass, Throwable cause) {
String excMsg;
if (cause == null) {
excMsg = "N/A";
} else if ((excMsg = ClassUtil.exceptionMessage(cause)) == null) {
excMsg = ClassUtil.nameOf(cause.getClass());
}
String msg = String.format("Cannot construct instance of %s, problem: %s",
ClassUtil.nameOf(instClass), excMsg);
// [databind#2162]: use specific exception type as we don't know if it's
// due to type definition, input, or neither
return ValueInstantiationException.from(_parser, msg, constructType(instClass), cause);
}
/**
* Helper method for constructing instantiation exception for specified type,
* to indicate that instantiation failed due to missing instantiator
* (creator; constructor or factory method).
*<p>
* Note that most of the time this method should NOT be called; instead,
* {@link #handleMissingInstantiator} should be called which will call this method
* if necessary.
*/
public DatabindException instantiationException(Class<?> instClass, String msg0) {
// [databind#2162]: use specific exception type as we don't know if it's
// due to type definition, input, or neither
return ValueInstantiationException.from(_parser,
String.format("Cannot construct instance of %s: %s",
ClassUtil.nameOf(instClass), msg0),
constructType(instClass));
}
@Override
public DatabindException invalidTypeIdException(JavaType baseType, String typeId,
String extraDesc) {
String msg = String.format("Could not resolve type id '%s' as a subtype of %s",
typeId, ClassUtil.getTypeDescription(baseType));
return InvalidTypeIdException.from(_parser, _colonConcat(msg, extraDesc), baseType, typeId);
}
public DatabindException missingTypeIdException(JavaType baseType,
String extraDesc) {
String msg = String.format("Could not resolve subtype of %s",
baseType);
return InvalidTypeIdException.from(_parser, _colonConcat(msg, extraDesc), baseType, null);
}
public DatabindException missingInjectableValueException(String msg,
Object valueId,
BeanProperty forProperty, Object beanInstance) {
return MissingInjectableValueExcepion.from(_parser, msg,
valueId, forProperty, beanInstance);
}
/*
/**********************************************************************
/* Other internal methods
/**********************************************************************
*/
/**
* Helper method to get a non-shared instance of {@link DateFormat} with default
* configuration; instance is lazily constructed, reused within same instance of
* context (that is, within same life-cycle of <code>readValue()</code> from mapper
* or reader). Reuse is safe since access will not occur from multiple threads
* (unless caller somehow manages to share context objects across threads which is not
* supported).
*/
protected DateFormat _getDateFormat() {
if (_dateFormat != null) {
return _dateFormat;
}
// 24-Feb-2012, tatu: At this point, all timezone configuration should have
// occurred, with respect to default date format and time zone configuration.
// But we still better clone an instance as formatters may be stateful.
DateFormat df = _config.getDateFormat();
_dateFormat = df = (DateFormat) df.clone();
return df;
}
/**
* Helper method for constructing description like "Object value" given
* {@link JsonToken} encountered.
*/
protected String _shapeForToken(JsonToken t) {
return JsonToken.valueDescFor(t);
}
}