SerializationContext.java
package tools.jackson.databind;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import tools.jackson.core.*;
import tools.jackson.core.io.CharacterEscapes;
import tools.jackson.core.tree.ArrayTreeNode;
import tools.jackson.core.tree.ObjectTreeNode;
import tools.jackson.core.util.JacksonFeatureSet;
import tools.jackson.databind.cfg.ContextAttributes;
import tools.jackson.databind.cfg.DatatypeFeature;
import tools.jackson.databind.cfg.DatatypeFeatures;
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.cfg.GeneratorSettings;
import tools.jackson.databind.exc.InvalidDefinitionException;
import tools.jackson.databind.exc.InvalidTypeIdException;
import tools.jackson.databind.introspect.Annotated;
import tools.jackson.databind.introspect.AnnotatedClass;
import tools.jackson.databind.introspect.AnnotatedMember;
import tools.jackson.databind.introspect.BeanPropertyDefinition;
import tools.jackson.databind.introspect.ClassIntrospector;
import tools.jackson.databind.jsontype.TypeSerializer;
import tools.jackson.databind.ser.*;
import tools.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap;
import tools.jackson.databind.ser.impl.TypeWrappedSerializer;
import tools.jackson.databind.ser.impl.UnknownSerializer;
import tools.jackson.databind.ser.std.NullSerializer;
import tools.jackson.databind.type.TypeFactory;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.databind.util.TokenBuffer;
/**
* Class that defines API used by {@link ObjectMapper} and
* {@link ValueSerializer}s to obtain serializers capable of serializing
* instances of specific types; as well as the default implementation
* of the functionality.
*<p>
* Provider handles caching aspects of serializer handling; all construction
* details are delegated to {@link SerializerFactory} instance.
*</p>
*<p>
* NOTE: In Jackson 2.x this class was called {@code SerializerProvider}.
*</p>
*/
public abstract class SerializationContext
extends DatabindContext
implements // NOTE: not JDK serializable with 3.x (factory that creates these is)
ObjectWriteContext // 3.0, for use by jackson-core
{
/**
* Placeholder serializer used when <code>java.lang.Object</code> typed property
* is marked to be serialized.
*<br>
* NOTE: starting with 2.6, this instance is NOT used for any other types, and
* separate instances are constructed for "empty" Beans.
*/
protected final static ValueSerializer<Object> DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer();
/*
/**********************************************************************
/* Configuration, general
/**********************************************************************
*/
/**
* Serialization configuration to use for serialization processing.
*/
protected final SerializationConfig _config;
/**
* Configuration to be used by streaming generator when it is constructed.
*
* @since 3.0
*/
protected final GeneratorSettings _generatorConfig;
/**
* Low-level {@link TokenStreamFactory} that may be used for constructing
* embedded generators.
*/
protected final TokenStreamFactory _streamFactory;
/**
* Token stream generator actively used; only set for per-call instances
*
* @since 3.0
*/
protected transient JsonGenerator _generator;
/**
* Capabilities of the output format.
*
* @since 3.0
*/
protected JacksonFeatureSet<StreamWriteCapability> _writeCapabilities;
/**
* View used for currently active serialization, if any.
*/
protected final Class<?> _activeView;
/*
/**********************************************************************
/* Configuration, serializer access
/**********************************************************************
*/
/**
* Factory used for constructing actual serializer instances.
* Only set for non-blueprint instances.
*/
protected final SerializerFactory _serializerFactory;
/**
* Serializer used to output a null value. Default implementation
* writes nulls using {@link JsonGenerator#writeNull}.
*/
protected final ValueSerializer<Object> _nullValueSerializer;
/**
* Flag set to indicate that we are using vanilla null value serialization
*/
protected final boolean _stdNullValueSerializer;
/*
/**********************************************************************
/* Helper objects for caching, reuse
/**********************************************************************
*/
/**
* Cache for doing type-to-value-serializer lookups.
*/
protected final SerializerCache _serializerCache;
/**
* For fast lookups, we will have a local non-shared read-only
* map that contains serializers previously fetched.
*/
protected final ReadOnlyClassToSerializerMap _knownSerializers;
/**
* Lazily acquired and instantiated formatter object: initialized
* first time it is needed, reused afterwards. Used via instances
* (not blueprints), so that access need not be thread-safe.
*/
protected DateFormat _dateFormat;
/**
* Lazily constructed {@link ClassIntrospector} instance: created from "blueprint"
*/
protected transient ClassIntrospector _classIntrospector;
/*
/**********************************************************************
/* Other state
/**********************************************************************
*/
/**
* Lazily-constructed holder for per-call attributes.
* Only set for non-blueprint instances.
*/
protected ContextAttributes _attributes;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
protected SerializationContext(TokenStreamFactory streamFactory,
SerializationConfig config, GeneratorSettings generatorConfig,
SerializerFactory f, SerializerCache cache)
{
_streamFactory = streamFactory;
_serializerFactory = f;
_config = config;
_generatorConfig = generatorConfig;
_serializerCache = cache;
// Default null key, value serializers configured via SerializerFactory
{
ValueSerializer<Object> ser = f.getDefaultNullValueSerializer();
if (ser == null) {
_stdNullValueSerializer = true;
ser = NullSerializer.instance;
} else {
_stdNullValueSerializer = false;
}
_nullValueSerializer = ser;
}
_activeView = config.getActiveView();
_attributes = config.getAttributes();
// Non-blueprint instances do have a read-only map; one that doesn't
// need synchronization for lookups.
_knownSerializers = _serializerCache.getReadOnlyLookupMap();
}
protected SerializationContext(SerializationContext src, SerializerCache serializerCache)
{
_streamFactory = src._streamFactory;
_serializerFactory = src._serializerFactory;
_config = src._config;
_generatorConfig = src._generatorConfig;
_serializerCache = serializerCache;
_stdNullValueSerializer = src._stdNullValueSerializer;
_nullValueSerializer = src._nullValueSerializer;
_activeView = src._activeView;
_attributes = src._attributes;
_knownSerializers = src._knownSerializers;
}
/*
/**********************************************************************
/* ObjectWriteContext impl, config access
/**********************************************************************
*/
@Override
public TokenStreamFactory tokenStreamFactory() {
return _streamFactory;
}
@Override
public FormatSchema getSchema() { return _generatorConfig.getSchema(); }
@Override
public CharacterEscapes getCharacterEscapes() { return _generatorConfig.getCharacterEscapes(); }
@Override
public PrettyPrinter getPrettyPrinter() {
PrettyPrinter pp = _generatorConfig.getPrettyPrinter();
if (pp == null) {
if (isEnabled(SerializationFeature.INDENT_OUTPUT)) {
pp = _config.constructDefaultPrettyPrinter();
}
}
return pp;
}
@Override
public boolean hasPrettyPrinter() {
return _generatorConfig.hasPrettyPrinter()
|| isEnabled(SerializationFeature.INDENT_OUTPUT);
}
@Override
public SerializableString getRootValueSeparator(SerializableString defaultSeparator) {
return _generatorConfig.getRootValueSeparator(defaultSeparator);
}
@Override
public int getStreamWriteFeatures(int defaults) {
return _config.getStreamWriteFeatures();
}
@Override
public int getFormatWriteFeatures(int defaults) {
return _config.getFormatWriteFeatures();
}
/*
/**********************************************************************
/* ObjectWriteContext impl, databind integration
/**********************************************************************
*/
@Override
public ArrayTreeNode createArrayNode() {
return _config.getNodeFactory().arrayNode();
}
@Override
public ObjectTreeNode createObjectNode() {
return _config.getNodeFactory().objectNode();
}
@Override
public void writeValue(JsonGenerator gen, Object value) throws JacksonException
{
// Let's keep track of active generator; useful mostly for error reporting...
JsonGenerator prevGen = _generator;
_assignGenerator(gen);
try {
if (value == null) {
if (_stdNullValueSerializer) { // minor perf optimization
gen.writeNull();
} else {
_nullValueSerializer.serialize(null, gen, this);
}
return;
}
Class<?> cls = value.getClass();
findTypedValueSerializer(cls, true).serialize(value, gen, this);
} finally {
_assignGenerator(prevGen);
}
}
@Override
public void writeTree(JsonGenerator gen, TreeNode tree) throws JacksonException
{
// 05-Oct-2017, tatu: Should probably optimize or something? Or not?
writeValue(gen, tree);
}
/*
/**********************************************************************
/* DatabindContext implementation (and closely related but ser-specific)
/**********************************************************************
*/
/**
* Method for accessing configuration for the serialization processing.
*/
@Override
public final SerializationConfig getConfig() { return _config; }
@Override
public final AnnotationIntrospector getAnnotationIntrospector() {
return _config.getAnnotationIntrospector();
}
@Override
public final TypeFactory getTypeFactory() {
return _config.getTypeFactory();
}
@Override
public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
throws IllegalArgumentException
{
if (baseType.hasRawClass(subclass)) {
return baseType;
}
// Need little bit different handling due to [databind#2632]; pass `true` for
// "relaxed" type assingment checks.
return getConfig().getTypeFactory().constructSpecializedType(baseType, subclass, true);
}
@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);
}
public final JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
return _config.getDefaultPropertyInclusion(baseType);
}
/**
* 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();
}
/*
/**********************************************************************
/* 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().introspectForSerialization(type, ac);
}
/*
/**********************************************************************
/* 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);
}
/*
/**********************************************************************
/* Generic attributes
/**********************************************************************
*/
@Override
public Object getAttribute(Object key) {
return _attributes.getAttribute(key);
}
@Override
public SerializationContext setAttribute(Object key, Object value)
{
_attributes = _attributes.withPerCallAttribute(key, value);
return this;
}
/*
/**********************************************************************
/* Access to other on/off features
/**********************************************************************
*/
/**
* Convenience method for checking whether specified serialization
* feature is enabled or not.
* Shortcut for:
*<pre>
* getConfig().isEnabled(feature);
*</pre>
*/
public final boolean isEnabled(SerializationFeature feature) {
return _config.isEnabled(feature);
}
/**
* "Bulk" access method for checking that all features specified by
* mask are enabled.
*/
public final boolean hasSerializationFeatures(int featureMask) {
return _config.hasSerializationFeatures(featureMask);
}
/**
* 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(StreamWriteCapability cap) {
Objects.requireNonNull(_writeCapabilities,
"_writeCapabilities not set for `SerializationContext`");
return _writeCapabilities.isEnabled(cap);
}
/*
/**********************************************************************
/* Access to other helper objects
/**********************************************************************
*/
/**
* Convenience method for accessing provider to find serialization filters used,
* equivalent to calling:
*<pre>
* getConfig().getFilterProvider();
*</pre>
*/
public final FilterProvider getFilterProvider() {
return _config.getFilterProvider();
}
public JsonGenerator getGenerator() {
return _generator;
}
/*
/**********************************************************************
/* Factory methods for getting appropriate TokenBuffer instances
/* (possibly overridden by backends for alternate data formats)
/**********************************************************************
*/
/**
* Specialized factory method used when we are converting values and do not
* typically have or use "real" parsers or generators.
*/
public TokenBuffer bufferForValueConversion() {
// 28-May-2021, tatu: Will directly call constructor from here, instead
// of adding a factory method, since alternate formats likely need to
// use different TokenBuffer sub[class:
// false -> no native Object Ids available (or rather not needed)
return new TokenBuffer(this, false);
}
/*
/**********************************************************************
/* Access to Object Id aspects
/**********************************************************************
*/
/**
* Method called to find the Object Id for given POJO, if one
* has been generated. Will always return a non-null Object;
* contents vary depending on whether an Object Id already
* exists or not.
*/
public abstract WritableObjectId findObjectId(Object forPojo,
ObjectIdGenerator<?> generatorType);
/*
/**********************************************************************
/* Serializer discovery: root/non-property value serializers
/**********************************************************************
*/
/**
* Method called to locate regular serializer, matching type serializer,
* and if both found, wrap them in a serializer that calls both in correct
* sequence. This method is mostly used for root-level serializer
* handling to allow for simpler caching. A call can always be replaced
* by equivalent calls to access serializer and type serializer separately.
* Note: contextualization (call to {@link ValueSerializer#createContextual}) is done
* before returning the {@link ValueSerializer}.
*
* @param rawType Type for purpose of locating a serializer; usually dynamic
* runtime type, but can also be static declared type, depending on configuration
* @param cache Whether resulting value serializer should be cached or not
*/
public ValueSerializer<Object> findTypedValueSerializer(Class<?> rawType,
boolean cache)
{
// First: do we have it cached?
ValueSerializer<Object> ser = _knownSerializers.typedValueSerializer(rawType);
if (ser != null) {
return ser;
}
// If not, compose from pieces:
JavaType fullType = _config.constructType(rawType);
ser = handleRootContextualization(findValueSerializer(rawType));
TypeSerializer typeSer = findTypeSerializer(fullType);
if (typeSer != null) {
typeSer = typeSer.forProperty(this, null);
ser = new TypeWrappedSerializer(typeSer, ser);
}
if (cache) {
_serializerCache.addTypedSerializer(rawType, ser);
}
return ser;
}
/**
* Method called to locate regular serializer, matching type serializer,
* and if both found, wrap them in a serializer that calls both in correct
* sequence. This method is mostly used for root-level serializer
* handling to allow for simpler caching. A call can always be replaced
* by equivalent calls to access serializer and type serializer separately.
* Note: contextualization (call to {@link ValueSerializer#createContextual}) is done
* before returning the {@link ValueSerializer}.
*
* @param valueType Declared type of value being serialized (which may not
* be actual runtime type); used for finding both value serializer and
* type serializer to use for adding polymorphic type (if any)
* @param cache Whether resulting value serializer should be cached or not
*/
public ValueSerializer<Object> findTypedValueSerializer(JavaType valueType, boolean cache)
{
ValueSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
if (ser != null) {
return ser;
}
ser = handleRootContextualization(findValueSerializer(valueType));
TypeSerializer typeSer = findTypeSerializer(valueType);
if (typeSer != null) {
typeSer = typeSer.forProperty(this, null);
ser = new TypeWrappedSerializer(typeSer, ser);
}
if (cache) {
_serializerCache.addTypedSerializer(valueType, ser);
}
return ser;
}
/**
* Method for finding (from cache) or creating (and caching) serializer for given type,
* without checking for polymorphic typing, and then contextualizing without actual
* property. This is most often used for root-level values (when writing
* sequences), but may sometimes be used for more esoteric value handling for
* delegation.
* Note: contextualization (call to {@link ValueSerializer#createContextual}) is done
* before returning the {@link ValueSerializer}.
*
* @since 3.0
*/
public ValueSerializer<Object> findRootValueSerializer(Class<?> rawType)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(rawType);
if (ser == null) {
JavaType fullType = _config.constructType(rawType);
ser = _serializerCache.untypedValueSerializer(fullType);
if (ser == null) {
ser = _createAndCacheUntypedSerializer(rawType, fullType);
}
}
return handleRootContextualization(ser);
}
/**
* Method for finding (from cache) or creating (and caching) serializer for given type,
* without checking for polymorphic typing, and then contextualizing without actual
* property. This is most often used for root-level values (when writing
* sequences), but may sometimes be used for more esoteric value handling for
* delegation.
* Note: contextualization (call to {@link ValueSerializer#createContextual}) is done
* before returning the {@link ValueSerializer}.
*
* @since 3.0
*/
public ValueSerializer<Object> findRootValueSerializer(JavaType valueType)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
if (ser == null) {
ser = _createAndCacheUntypedSerializer(valueType);
}
return handleRootContextualization(ser);
}
/*
/**********************************************************************
/* Serializer discovery: property value serializers
/**********************************************************************
*/
/**
* Method used for locating "primary" property value serializer (one directly
* handling value of the property). Difference (if any) has to do with contextual resolution,
* and method(s) called: this method should only be called when caller is
* certain that this is the primary property value serializer.
* Contextualization (call to {@link ValueSerializer#createContextual(SerializationContext, BeanProperty)}
* will be done before returning the {@link ValueSerializer}.
*
* @param property Property that is being handled; will never be null, and its
* type has to match <code>valueType</code> parameter.
*/
public ValueSerializer<Object> findPrimaryPropertySerializer(JavaType valueType,
BeanProperty property)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
if (ser == null) {
ser = _createAndCachePropertySerializer(valueType, property);
} else if (property != null) {
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(valueType);
// [databind#5405]: property-level @JsonFormat must be honored even with cached serializers
ser = _checkShapeShifting(valueType, beanDescRef, property, ser);
}
return handlePrimaryContextualization(ser, property);
}
/**
* @see #findPrimaryPropertySerializer(JavaType, BeanProperty)
*/
public ValueSerializer<Object> findPrimaryPropertySerializer(Class<?> rawType,
BeanProperty property)
{
boolean checkShape = (property != null);
JavaType fullType = null;
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(rawType);
if (ser == null) {
fullType = _config.constructType(rawType);
ser = _serializerCache.untypedValueSerializer(fullType);
if (ser == null) {
checkShape = false; // because next call does it
ser = _createAndCachePropertySerializer(rawType, fullType, property);
}
}
if (checkShape) {
if (fullType == null) {
fullType = _config.constructType(rawType);
}
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(fullType);
// [databind#5405]: property-level @JsonFormat must be honored even with cached serializers
ser = _checkShapeShifting(fullType, beanDescRef, property, ser);
}
return handlePrimaryContextualization(ser, property);
}
/**
* Method similar to {@link #findPrimaryPropertySerializer(JavaType, BeanProperty)}
* but used for "content values", secondary types used by "primary" serializers
* for structured types like Arrays, {@link java.util.Collection}s, {@link java.util.Map}s
* and so on.
*<p>
* Serializer will be contextualized, but will not have type serializer wrapped.
*
* @param valueType Type of (secondary / content) values being serialized
* @param property (optional) Property that refers to values via primary type (so type
* DOES NOT necessarily match {@code valueType})
*/
public ValueSerializer<Object> findContentValueSerializer(JavaType valueType,
BeanProperty property)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
if (ser == null) {
ser = _createAndCachePropertySerializer(valueType, property);
}
return handleSecondaryContextualization(ser, property);
}
/**
* See {@link #findContentValueSerializer(JavaType, BeanProperty)}.
*/
public ValueSerializer<Object> findContentValueSerializer(Class<?> rawType,
BeanProperty property)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(rawType);
if (ser == null) {
JavaType fullType = _config.constructType(rawType);
ser = _serializerCache.untypedValueSerializer(fullType);
if (ser == null) {
ser = _createAndCachePropertySerializer(rawType, fullType, property);
}
}
return handleSecondaryContextualization(ser, property);
}
/*
/**********************************************************************
/* General serializer locating functionality
/**********************************************************************
*/
/**
* @see #findValueSerializer(JavaType)
*/
public ValueSerializer<Object> findValueSerializer(Class<?> rawType)
{
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(rawType);
if (ser == null) {
JavaType fullType = _config.constructType(rawType);
ser = _serializerCache.untypedValueSerializer(fullType);
if (ser == null) {
ser = _createAndCacheUntypedSerializer(rawType, fullType);
}
}
return ser;
}
/**
* Method variant used when we do NOT want contextualization to happen; it will need
* to be done at a later point (many serializers are not in operational state before
* contextualization, call to {@link ValueSerializer#createContextual(SerializationContext, BeanProperty)}),
* but caller wants to be able to do that at a later point; sometimes to avoid infinite loops
*/
public ValueSerializer<Object> findValueSerializer(JavaType valueType)
{
// (see comments from above method)
ValueSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
if (ser == null) {
ser = _createAndCacheUntypedSerializer(valueType);
}
return ser;
}
/*
/**********************************************************************
/* Serializer discovery: type serializers
/**********************************************************************
*/
/**
* Method called to get the {@link TypeSerializer} to use for including Type Id necessary
* for serializing for the given Java class.
* Useful for schema generators.
*/
public TypeSerializer findTypeSerializer(JavaType baseType) {
return findTypeSerializer(baseType, introspectClassAnnotations(baseType));
}
/**
* Method called to get the {@link TypeSerializer} to use for including Type Id necessary
* for serializing for the given Java class.
* Useful for schema generators.
*
* @since 3.0
*/
public TypeSerializer findTypeSerializer(JavaType baseType, AnnotatedClass classAnnotations)
{
return _config.getTypeResolverProvider().findTypeSerializer(this, baseType,
classAnnotations);
}
/**
* Like {@link #findTypeSerializer(JavaType)}, but for use from specific POJO property.
* Method called to create a type information serializer for values of given
* non-container property
* if one is needed. If not needed (no polymorphic handling configured), should
* return null.
*
* @param baseType Declared type to use as the base type for type information serializer
*
* @return Type serializer to use for property values, if one is needed; null if not.
*
* @since 3.0
*/
public TypeSerializer findPropertyTypeSerializer(JavaType baseType, AnnotatedMember accessor)
{
return _config.getTypeResolverProvider()
.findPropertyTypeSerializer(this, accessor, baseType);
}
/*
/**********************************************************************
/* Serializer discovery: key serializers
/**********************************************************************
*/
/**
* Method called to get the serializer to use for serializing
* non-null Map keys. Separation from regular
* {@link #findValueSerializer} method is because actual write
* method must be different (@link JsonGenerator#writeName};
* but also since behavior for some key types may differ.
*<p>
* Note that the serializer itself can be called with instances
* of any Java object, but not nulls.
*/
public ValueSerializer<Object> findKeySerializer(JavaType keyType, BeanProperty property)
{
// 16-Mar-2018, tatu: Used to have "default key serializer" in 2.x; dropped to let/make
// custom code use Module interface or similar to provide key serializers
ValueSerializer<Object> ser = _serializerFactory.createKeySerializer(this, keyType);
// _handleContextualResolvable(ser, property):
ser.resolve(this);
return handleSecondaryContextualization(ser, property);
}
public ValueSerializer<Object> findKeySerializer(Class<?> rawKeyType, BeanProperty property)
{
return findKeySerializer(_config.constructType(rawKeyType), property);
}
public ValueSerializer<Object> getDefaultNullValueSerializer() {
return _nullValueSerializer;
}
/**
* Method called to find a serializer to use for null values for given
* declared type. Note that type is completely based on declared type,
* since nulls in Java have no type and thus runtime type cannot be
* determined.
*/
public ValueSerializer<Object> findNullKeySerializer(JavaType serializationType,
BeanProperty property)
{
// rarely needed (that is, not on critical perf path), delegate to factory
return _serializerFactory.getDefaultNullKeySerializer();
}
/*
/**********************************************************************
/* Serializer discovery: other misc serializers, null value, unknown
/**********************************************************************
*/
/**
* Method called to get the serializer to use for serializing null
* values for specified property.
*<p>
* Default implementation simply calls {@link #getDefaultNullValueSerializer()};
* can be overridden to add custom null serialization for properties
* of certain type or name. This gives method full granularity to basically
* override null handling for any specific property or class of properties.
*/
public ValueSerializer<Object> findNullValueSerializer(BeanProperty property)
{
return _nullValueSerializer;
}
/**
* Method called to get the serializer to use if provider
* cannot determine an actual type-specific serializer
* to use; typically when none of {@link SerializerFactory}
* instances are able to construct a serializer.
*<p>
* Typically, returned serializer will throw an exception,
* although alternatively {@link tools.jackson.databind.ser.std.ToStringSerializer}
* could be returned as well.
*
* @param unknownType Type for which no serializer is found
*/
public ValueSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
// 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class
if (unknownType == Object.class) {
return DEFAULT_UNKNOWN_SERIALIZER;
}
// otherwise construct explicit instance with property handled type
return new UnknownSerializer(unknownType);
}
/**
* Helper method called to see if given serializer is considered to be
* something returned by {@link #getUnknownTypeSerializer}, that is, something
* for which no regular serializer was found or constructed.
*/
public boolean isUnknownTypeSerializer(ValueSerializer<?> ser) {
if ((ser == DEFAULT_UNKNOWN_SERIALIZER) || (ser == null)) {
return true;
}
// 23-Apr-2015, tatu: "empty" serializer is trickier; needs to consider
// error handling
if (isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
if (ser instanceof UnknownSerializer) {
return true;
}
}
return false;
}
/*
/**********************************************************************
/* Low-level methods for actually constructing and initializing serializers
/**********************************************************************
*/
/**
* Method that will try to construct a value serializer; and if
* one is successfully created, cache it for reuse.
*/
protected ValueSerializer<Object> _createAndCacheUntypedSerializer(Class<?> rawType,
JavaType fullType)
{
// Important: must introspect all annotations, not just class
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(fullType);
ValueSerializer<Object> ser;
try {
ser = _serializerFactory.createSerializer(this, fullType, beanDescRef, null);
} catch (IllegalArgumentException iae) {
// We better only expose checked exceptions, since those are what caller is expected to handle
reportBadTypeDefinition(beanDescRef, ClassUtil.exceptionMessage(iae));
ser = null; // never gets here
}
// Always cache -- and in this case both for raw and full type
_serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this);
return ser;
}
protected ValueSerializer<Object> _createAndCacheUntypedSerializer(JavaType type)
{
// Important: must introspect all annotations, not just class
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(type);
ValueSerializer<Object> ser;
try {
ser = _serializerFactory.createSerializer(this, type, beanDescRef, null);
} catch (IllegalArgumentException iae) {
// We better only expose checked exceptions, since those are what caller is expected to handle
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
}
// always cache -- but only full type (may be parameterized)
_serializerCache.addAndResolveNonTypedSerializer(type, ser, this);
return ser;
}
/**
* Alternative to {@link #_createAndCacheUntypedSerializer(Class, JavaType)}, used
* when serializer is requested for given property.
*/
protected ValueSerializer<Object> _createAndCachePropertySerializer(Class<?> rawType,
JavaType fullType, BeanProperty prop)
{
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(fullType);
ValueSerializer<Object> ser;
try {
ser = _serializerFactory.createSerializer(this, fullType, beanDescRef, null);
} catch (IllegalArgumentException iae) {
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
}
_serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this);
// Fine, we have to base instance. But how about per-property format overrides?
if (prop == null) {
return ser;
}
return _checkShapeShifting(fullType, beanDescRef, prop, ser);
}
/**
* Alternative to {@link #_createAndCacheUntypedSerializer(JavaType)}, used
* when serializer is requested for given property.
*/
protected ValueSerializer<Object> _createAndCachePropertySerializer(JavaType type,
BeanProperty prop)
{
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(type);
ValueSerializer<Object> ser;
try {
ser = _serializerFactory.createSerializer(this, type, beanDescRef, null);
} catch (IllegalArgumentException iae) {
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
}
_serializerCache.addAndResolveNonTypedSerializer(type, ser, this);
// Fine, we have to base instance. But how about per-property format overrides?
if (prop == null) {
return ser;
}
return _checkShapeShifting(type, beanDescRef, prop, ser);
}
@SuppressWarnings("unchecked")
private ValueSerializer<Object> _checkShapeShifting(JavaType type,
BeanDescription.Supplier beanDescRef, BeanProperty prop, ValueSerializer<?> ser)
{
JsonFormat.Value overrides = prop.findFormatOverrides(_config);
if (overrides != null && overrides != JsonFormat.Value.empty()) {
// First: it may be completely fine to use serializer, despite some overrides
ValueSerializer<?> ser2 = ser.withFormatOverrides(_config, overrides);
if (ser2 != null) {
ser = ser2;
} else {
// But if not, we need to re-create it via factory
ser = _serializerFactory.createSerializer(this, type, beanDescRef, overrides);
}
}
return (ValueSerializer<Object>) ser;
}
@SuppressWarnings("unchecked")
protected ValueSerializer<Object> _handleResolvable(ValueSerializer<?> ser)
{
ser.resolve(this);
return (ValueSerializer<Object>) ser;
}
/*
/**********************************************************************
/* Methods for creating instances based on annotations
/**********************************************************************
*/
/**
* Method that can be called to construct and configure serializer instance,
* either given a {@link Class} to instantiate (with default constructor),
* or an uninitialized serializer instance.
* Either way, serializer will be properly resolved
* (via {@link tools.jackson.databind.ValueSerializer#resolve}).
*
* @param annotated Annotated entity that contained definition
* @param serDef Serializer definition: either an instance or class
*/
public abstract ValueSerializer<Object> serializerInstance(Annotated annotated,
Object serDef);
/**
* Method that can be called to construct and configure {@link JsonInclude}
* filter instance,
* given a {@link Class} to instantiate (with default constructor, by default).
*
* @param forProperty (optional) If filter is created for a property, that property;
* `null` if filter created via defaulting, global or per-type.
*/
public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty,
Class<?> filterClass);
/**
* Follow-up method that may be called after calling {@link #includeFilterInstance},
* to check handling of `null` values by the filter.
*/
public abstract boolean includeFilterSuppressNulls(Object filter);
/*
/**********************************************************************
/* Support for contextualization
/**********************************************************************
*/
/**
* Method called for primary property serializers (ones
* directly created to serialize values of a POJO property),
* to handle details of contextualization, calling
* {@link ValueSerializer#createContextual(SerializationContext, BeanProperty)} with given property context.
*
* @param property Property for which the given primary serializer is used; never null.
*/
@SuppressWarnings("unchecked")
public ValueSerializer<Object> handlePrimaryContextualization(ValueSerializer<?> ser,
BeanProperty property)
{
if (ser != null) {
ser = ser.createContextual(this, property);
}
return (ValueSerializer<Object>) ser;
}
/**
* Method called for secondary property serializers (ones
* NOT directly created to serialize values of a POJO property
* but instead created as a dependant serializer -- such as value serializers
* for structured types, or serializers for root values)
* to handle details of contextualization, calling
* {@link ValueSerializer#createContextual(SerializationContext, BeanProperty)} with given property context.
* Given that these serializers 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 property Property for which serializer is used, if any; null
* when deserializing root values
*/
@SuppressWarnings("unchecked")
public ValueSerializer<Object> handleSecondaryContextualization(ValueSerializer<?> ser,
BeanProperty property)
{
if (ser != null) {
ser = ser.createContextual(this, property);
}
return (ValueSerializer<Object>) ser;
}
/**
* @since 3.0
*/
@SuppressWarnings("unchecked")
public ValueSerializer<Object> handleRootContextualization(ValueSerializer<?> ser)
{
if (ser != null) {
ser = ser.createContextual(this, null);
}
return (ValueSerializer<Object>) ser;
}
/*
/**********************************************************************
/* Convenience methods for serializing using default methods
/**********************************************************************
*/
/**
* Convenience method that will serialize given property with specified
* value, using the default serializer for runtime type of {@code value}.
*/
public final void defaultSerializeProperty(String propertyName, Object value, JsonGenerator g)
throws JacksonException
{
g.writeName(propertyName);
writeValue(g, value);
}
/**
* Method that will handle serialization of Date(-like) values, using
* {@link SerializationConfig} settings to determine expected serialization
* behavior.
* Note: date here means "full" date, that is, date AND time, as per
* Java convention (and not date-only values like in SQL)
*/
public final void defaultSerializeDateValue(long timestamp, JsonGenerator g)
throws JacksonException
{
if (isEnabled(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)) {
g.writeNumber(timestamp);
} else {
g.writeString(_dateFormat().format(new Date(timestamp)));
}
}
/**
* Method that will handle serialization of Date(-like) values, using
* {@link SerializationConfig} settings to determine expected serialization
* behavior.
* Note: date here means "full" date, that is, date AND time, as per
* Java convention (and not date-only values like in SQL)
*/
public final void defaultSerializeDateValue(Date date, JsonGenerator g)
throws JacksonException
{
if (isEnabled(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)) {
g.writeNumber(date.getTime());
} else {
g.writeString(_dateFormat().format(date));
}
}
/**
* Method that will handle serialization of Dates used as {@link java.util.Map} keys,
* based on {@link DateTimeFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS}
* value (and if using textual representation, configured date format)
*/
public void defaultSerializeDateKey(long timestamp, JsonGenerator g)
throws JacksonException
{
if (isEnabled(DateTimeFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) {
g.writeName(String.valueOf(timestamp));
} else {
g.writeName(_dateFormat().format(new Date(timestamp)));
}
}
/**
* Method that will handle serialization of Dates used as {@link java.util.Map} keys,
* based on {@link DateTimeFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS}
* value (and if using textual representation, configured date format)
*/
public void defaultSerializeDateKey(Date date, JsonGenerator g) throws JacksonException
{
if (isEnabled(DateTimeFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) {
g.writeName(String.valueOf(date.getTime()));
} else {
g.writeName(_dateFormat().format(date));
}
}
/**
* Method to call when serializing a {@code null} value (POJO property, Map entry value,
* Collection/array element) using configured standard mechanism. Note that this
* does NOT consider filtering any more as value is expected.
*
* @since 3.0 (in 2.x was called <code>defaultSerializeNull</code>)
*/
public final void defaultSerializeNullValue(JsonGenerator g) throws JacksonException
{
if (_stdNullValueSerializer) { // minor perf optimization
g.writeNull();
} else {
_nullValueSerializer.serialize(null, g, this);
}
}
/*
/**********************************************************************
/* Serialization-like helper methods
/**********************************************************************
*/
/**
* Method that will convert given Java value (usually bean) into its
* equivalent Tree mode {@link JsonNode} representation.
* Functionally similar to serializing value into token stream and parsing that
* stream back as tree model node,
* but more efficient as {@link TokenBuffer} is used to contain the intermediate
* representation instead of fully serialized contents.
*<p>
* NOTE: while results are usually identical to that of serialization followed
* by deserialization, this is not always the case. In some cases serialization
* into intermediate representation will retain encapsulation of things like
* raw value ({@link tools.jackson.databind.util.RawValue}) or basic
* node identity ({@link JsonNode}). If so, result is a valid tree, but values
* are not re-constructed through actual format representation. So if transformation
* requires actual materialization of encoded content,
* it will be necessary to do actual serialization.
*
* @param <T> Actual node type; usually either basic {@link JsonNode} or
* {@link tools.jackson.databind.node.ObjectNode}
* @param fromValue Java value to convert
*
* @return (non-null) Root node of the resulting content tree: in case of
* {@code null} value node for which {@link JsonNode#isNull()} returns {@code true}.
*/
public abstract <T extends JsonNode> T valueToTree(Object fromValue)
throws JacksonException;
/*
/**********************************************************************
/* Error reporting
/**********************************************************************
*/
/**
* 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 InvalidDefinitionException}.
*/
@Override
public <T> T reportBadTypeDefinition(BeanDescription bean,
String msg, Object... msgArgs)
throws DatabindException
{
String beanDesc = "N/A";
if (bean != null) {
beanDesc = ClassUtil.nameOf(bean.getBeanClass());
}
msg = String.format("Invalid type definition for type %s: %s",
beanDesc, _format(msg, msgArgs));
throw InvalidDefinitionException.from(getGenerator(), 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 InvalidDefinitionException}.
*/
public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
String message, Object... msgArgs)
throws DatabindException
{
message = _format(message, msgArgs);
String propName = "N/A";
if (prop != null) {
propName = _quotedString(prop.getName());
}
String beanDesc = "N/A";
if (bean != null) {
beanDesc = ClassUtil.nameOf(bean.getBeanClass());
}
message = String.format("Invalid definition for property %s (of type %s): %s",
propName, beanDesc, message);
throw InvalidDefinitionException.from(getGenerator(), message, bean, prop);
}
@Override
public <T> T reportBadDefinition(JavaType type, String msg) throws DatabindException {
throw InvalidDefinitionException.from(getGenerator(), msg, type);
}
public <T> T reportBadDefinition(JavaType type, String msg, Throwable cause)
throws DatabindException
{
throw InvalidDefinitionException.from(getGenerator(), msg, type)
.withCause(cause);
}
public <T> T reportBadDefinition(Class<?> raw, String msg, Throwable cause)
throws DatabindException
{
throw InvalidDefinitionException.from(getGenerator(), msg, constructType(raw))
.withCause(cause);
}
/**
* Helper method called to indicate problem; default behavior is to construct and
* throw a {@link DatabindException}, but in future may collect more than one
* and only throw after certain number, or at the end of serialization.
*/
public void reportMappingProblem(Throwable t, String message, Object... msgArgs)
throws DatabindException
{
throw _mappingProblem(t, message, msgArgs);
}
protected DatabindException _mappingProblem(Throwable t, String message, Object... msgArgs)
{
return DatabindException.from(getGenerator(), _format(message, msgArgs), t);
}
/**
* Helper method called to indicate problem; default behavior is to construct and
* throw a {@link DatabindException}, but in future may collect more than one
* and only throw after certain number, or at the end of serialization.
*/
public void reportMappingProblem(String message, Object... msgArgs)
throws DatabindException
{
throw DatabindException.from(getGenerator(), _format(message, msgArgs));
}
@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(null, _colonConcat(msg, extraDesc), baseType, typeId);
}
protected void _reportIncompatibleRootType(Object value, JavaType rootType)
throws JacksonException
{
// One special case: allow primitive/wrapper type coercion
if (rootType.isPrimitive()) {
Class<?> wrapperType = ClassUtil.wrapperType(rootType.getRawClass());
// If it's just difference between wrapper, primitive, let it slide
if (wrapperType.isAssignableFrom(value.getClass())) {
return;
}
}
reportBadDefinition(rootType, String.format(
"Incompatible types: declared root type (%s) vs %s",
rootType, ClassUtil.classNameOf(value)));
}
/*
/**********************************************************************
/* Internal methods, other
/**********************************************************************
*/
/**
* Method called to assign the generator to use for writing output; also
* updates configuration settings that depend on generator.
*
* @param g Generator to use for writing output
*/
protected void _assignGenerator(JsonGenerator g) {
_generator = g;
_writeCapabilities = (g == null) ? null : g.streamWriteCapabilities();
}
protected final DateFormat _dateFormat()
{
if (_dateFormat != null) {
return _dateFormat;
}
// At this point, all timezone configuration should have occurred, with respect
// to default dateformat configuration. But we still better clone
// an instance as formatters are stateful, not thread-safe.
DateFormat df = _config.getDateFormat();
_dateFormat = df = (DateFormat) df.clone();
// [databind#939]: 26-Sep-2015, tatu: With 2.6, formatter has been (pre)configured
// with TimeZone, so we should NOT try overriding it unlike with earlier versions
/*
TimeZone tz = getTimeZone();
if (tz != df.getTimeZone()) {
df.setTimeZone(tz);
}
*/
return df;
}
}