package com.fasterxml.jackson.databind.ser;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.impl.*;
import com.fasterxml.jackson.databind.ser.std.*;
import com.fasterxml.jackson.databind.type.*;
import com.fasterxml.jackson.databind.util.*;
Factory class that can provide serializers for standard JDK classes, as well as custom classes that extend standard classes or implement one of "well-known" interfaces (such as Collection
).
Since all the serializers are eagerly instantiated, and there is
no additional introspection or customizability of these types,
this factory is essentially stateless.
/**
* Factory class that can provide serializers for standard JDK classes,
* as well as custom classes that extend standard classes or implement
* one of "well-known" interfaces (such as {@link java.util.Collection}).
*<p>
* Since all the serializers are eagerly instantiated, and there is
* no additional introspection or customizability of these types,
* this factory is essentially stateless.
*/
@SuppressWarnings("serial")
public abstract class BasicSerializerFactory
extends SerializerFactory
implements java.io.Serializable
{
/*
/**********************************************************
/* Configuration, lookup tables/maps
/**********************************************************
*/
Since these are all JDK classes, we shouldn't have to worry
about ClassLoader used to load them. Rather, we can just
use the class name, and keep things simple and efficient.
/**
* Since these are all JDK classes, we shouldn't have to worry
* about ClassLoader used to load them. Rather, we can just
* use the class name, and keep things simple and efficient.
*/
protected final static HashMap<String, JsonSerializer<?>> _concrete;
Actually it may not make much sense to eagerly instantiate all
kinds of serializers: so this Map actually contains class references,
not instances
/**
* Actually it may not make much sense to eagerly instantiate all
* kinds of serializers: so this Map actually contains class references,
* not instances
*/
protected final static HashMap<String, Class<? extends JsonSerializer<?>>> _concreteLazy;
static {
HashMap<String, Class<? extends JsonSerializer<?>>> concLazy
= new HashMap<String, Class<? extends JsonSerializer<?>>>();
HashMap<String, JsonSerializer<?>> concrete
= new HashMap<String, JsonSerializer<?>>();
/* String and string-like types (note: date types explicitly
* not included -- can use either textual or numeric serialization)
*/
concrete.put(String.class.getName(), new StringSerializer());
final ToStringSerializer sls = ToStringSerializer.instance;
concrete.put(StringBuffer.class.getName(), sls);
concrete.put(StringBuilder.class.getName(), sls);
concrete.put(Character.class.getName(), sls);
concrete.put(Character.TYPE.getName(), sls);
// Primitives/wrappers for primitives (primitives needed for Beans)
NumberSerializers.addAll(concrete);
concrete.put(Boolean.TYPE.getName(), new BooleanSerializer(true));
concrete.put(Boolean.class.getName(), new BooleanSerializer(false));
// Other numbers, more complicated
concrete.put(BigInteger.class.getName(), new NumberSerializer(BigInteger.class));
concrete.put(BigDecimal.class.getName(),new NumberSerializer(BigDecimal.class));
// Other discrete non-container types:
// First, Date/Time zoo:
concrete.put(Calendar.class.getName(), CalendarSerializer.instance);
concrete.put(java.util.Date.class.getName(), DateSerializer.instance);
// And then other standard non-structured JDK types
for (Map.Entry<Class<?>,Object> en : StdJdkSerializers.all()) {
Object value = en.getValue();
if (value instanceof JsonSerializer<?>) {
concrete.put(en.getKey().getName(), (JsonSerializer<?>) value);
} else {
@SuppressWarnings("unchecked")
Class<? extends JsonSerializer<?>> cls = (Class<? extends JsonSerializer<?>>) value;
concLazy.put(en.getKey().getName(), cls);
}
}
// Jackson-specific type(s)
// (Q: can this ever be sub-classed?)
concLazy.put(TokenBuffer.class.getName(), TokenBufferSerializer.class);
_concrete = concrete;
_concreteLazy = concLazy;
}
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
Configuration settings for this factory; immutable instance (just like this
factory), new version created via copy-constructor (fluent-style)
/**
* Configuration settings for this factory; immutable instance (just like this
* factory), new version created via copy-constructor (fluent-style)
*/
protected final SerializerFactoryConfig _factoryConfig;
/*
/**********************************************************
/* Life cycle
/**********************************************************
*/
We will provide default constructor to allow sub-classing,
but make it protected so that no non-singleton instances of
the class will be instantiated.
/**
* We will provide default constructor to allow sub-classing,
* but make it protected so that no non-singleton instances of
* the class will be instantiated.
*/
protected BasicSerializerFactory(SerializerFactoryConfig config) {
_factoryConfig = (config == null) ? new SerializerFactoryConfig() : config;
}
Method for getting current SerializerFactoryConfig
.
Note that since instances are immutable, you can NOT change settings
by accessing an instance and calling methods: this will simply create
new instance of config object.
/**
* Method for getting current {@link SerializerFactoryConfig}.
*<p>
* Note that since instances are immutable, you can NOT change settings
* by accessing an instance and calling methods: this will simply create
* new instance of config object.
*/
public SerializerFactoryConfig getFactoryConfig() {
return _factoryConfig;
}
Method used for creating a new instance of this factory, but with different
configuration. Reason for specifying factory method (instead of plain constructor)
is to allow proper sub-classing of factories.
Note that custom sub-classes generally must override implementation of this method, as it usually requires instantiating a new instance of factory type. Check out javadocs for BeanSerializerFactory
for more details.
/**
* Method used for creating a new instance of this factory, but with different
* configuration. Reason for specifying factory method (instead of plain constructor)
* is to allow proper sub-classing of factories.
*<p>
* Note that custom sub-classes generally <b>must override</b> implementation
* of this method, as it usually requires instantiating a new instance of
* factory type. Check out javadocs for
* {@link com.fasterxml.jackson.databind.ser.BeanSerializerFactory} for more details.
*/
public abstract SerializerFactory withConfig(SerializerFactoryConfig config);
Convenience method for creating a new factory instance with an additional
serializer provider.
/**
* Convenience method for creating a new factory instance with an additional
* serializer provider.
*/
@Override
public final SerializerFactory withAdditionalSerializers(Serializers additional) {
return withConfig(_factoryConfig.withAdditionalSerializers(additional));
}
Convenience method for creating a new factory instance with an additional
key serializer provider.
/**
* Convenience method for creating a new factory instance with an additional
* key serializer provider.
*/
@Override
public final SerializerFactory withAdditionalKeySerializers(Serializers additional) {
return withConfig(_factoryConfig.withAdditionalKeySerializers(additional));
}
Convenience method for creating a new factory instance with additional bean
serializer modifier.
/**
* Convenience method for creating a new factory instance with additional bean
* serializer modifier.
*/
@Override
public final SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) {
return withConfig(_factoryConfig.withSerializerModifier(modifier));
}
/*
/**********************************************************
/* SerializerFactory impl
/**********************************************************
*/
// Implemented by sub-classes
@Override
public abstract JsonSerializer<Object> createSerializer(SerializerProvider prov,
JavaType type)
throws JsonMappingException;
@Override // since 2.11
@SuppressWarnings("unchecked")
public JsonSerializer<Object> createKeySerializer(SerializerProvider ctxt,
JavaType keyType, JsonSerializer<Object> defaultImpl) throws JsonMappingException
{
// 16-Oct-2019, tatu: use to only introspect class annotations but we'll
// need methods too for `@JsonValue` later; and custom lookup might want it, too
final SerializationConfig config = ctxt.getConfig();
BeanDescription beanDesc = config.introspect(keyType);
JsonSerializer<?> ser = null;
// Minor optimization: to avoid constructing beanDesc, bail out if none registered
if (_factoryConfig.hasKeySerializers()) {
// Only thing we have here are module-provided key serializers:
for (Serializers serializers : _factoryConfig.keySerializers()) {
ser = serializers.findSerializer(config, keyType, beanDesc);
if (ser != null) {
break;
}
}
}
if (ser == null) {
// [databind#2452]: Support `@JsonSerialize(keyUsing = ...)` -- new in 2.11
ser = _findKeySerializer(ctxt, beanDesc.getClassInfo());
if (ser == null) {
ser = defaultImpl;
if (ser == null) {
ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false);
// As per [databind#47], also need to support @JsonValue
if (ser == null) {
AnnotatedMember am = beanDesc.findJsonValueAccessor();
if (am != null) {
final Class<?> rawType = am.getRawType();
JsonSerializer<?> delegate = StdKeySerializers.getStdKeySerializer(config,
rawType, true);
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(am.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
ser = new JsonValueSerializer(am, delegate);
} else {
ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass());
}
}
}
}
}
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyKeySerializer(config, keyType, beanDesc, ser);
}
}
return (JsonSerializer<Object>) ser;
}
// Old method: to be removed from 3.0 -- similar to above but can't look for "keyUsing"
@Override
@SuppressWarnings("unchecked")
@Deprecated // since 2.11
public JsonSerializer<Object> createKeySerializer(SerializationConfig config,
JavaType keyType, JsonSerializer<Object> defaultImpl)
{
BeanDescription beanDesc = config.introspect(keyType);
JsonSerializer<?> ser = null;
if (_factoryConfig.hasKeySerializers()) {
for (Serializers serializers : _factoryConfig.keySerializers()) {
ser = serializers.findSerializer(config, keyType, beanDesc);
if (ser != null) {
break;
}
}
}
if (ser == null) {
ser = defaultImpl;
if (ser == null) {
ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false);
if (ser == null) {
AnnotatedMember am = beanDesc.findJsonValueAccessor();
if (am != null) {
final Class<?> rawType = am.getRawType();
JsonSerializer<?> delegate = StdKeySerializers.getStdKeySerializer(config,
rawType, true);
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(am.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
ser = new JsonValueSerializer(am, delegate);
} else {
ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass());
}
}
}
}
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyKeySerializer(config, keyType, beanDesc, ser);
}
}
return (JsonSerializer<Object>) ser;
}
Method called to construct a type serializer for values with given declared
base type. This is called for values other than those of bean property
types.
/**
* Method called to construct a type serializer for values with given declared
* base type. This is called for values other than those of bean property
* types.
*/
@Override
public TypeSerializer createTypeSerializer(SerializationConfig config,
JavaType baseType)
{
BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass());
AnnotatedClass ac = bean.getClassInfo();
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType);
/* Ok: if there is no explicit type info handler, we may want to
* use a default. If so, config object knows what to use.
*/
Collection<NamedType> subtypes = null;
if (b == null) {
b = config.getDefaultTyper(baseType);
} else {
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, ac);
}
if (b == null) {
return null;
}
// 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing
// wrt EXTERNAL_PROPERTY
return b.buildTypeSerializer(config, baseType, subtypes);
}
/*
/**********************************************************
/* Additional API for other core classes
/**********************************************************
*/
protected abstract Iterable<Serializers> customSerializers();
/*
/**********************************************************
/* Overridable secondary serializer accessor methods
/**********************************************************
*/
Method that will use fast lookup (and identity comparison) methods to
see if we know serializer to use for given type.
/**
* Method that will use fast lookup (and identity comparison) methods to
* see if we know serializer to use for given type.
*/
protected final JsonSerializer<?> findSerializerByLookup(JavaType type,
SerializationConfig config, BeanDescription beanDesc,
boolean staticTyping)
{
Class<?> raw = type.getRawClass();
String clsName = raw.getName();
JsonSerializer<?> ser = _concrete.get(clsName);
if (ser == null) {
Class<? extends JsonSerializer<?>> serClass = _concreteLazy.get(clsName);
if (serClass != null) {
// 07-Jan-2017, tatu: Should never fail (since we control constructors),
// but if it does will throw `IllegalArgumentException` with description,
// which we could catch, re-title.
return ClassUtil.createInstance(serClass, false);
}
}
return ser;
}
Method called to see if one of primary per-class annotations (or related, like implementing of JsonSerializable
) determines the serializer to use.
Currently handles things like:
- If type implements
JsonSerializable
, use that
- If type has
JsonValue
annotation (or equivalent), build serializer based on that property
Since: 2.0
/**
* Method called to see if one of primary per-class annotations
* (or related, like implementing of {@link JsonSerializable})
* determines the serializer to use.
*<p>
* Currently handles things like:
*<ul>
* <li>If type implements {@link JsonSerializable}, use that
* </li>
* <li>If type has {@link com.fasterxml.jackson.annotation.JsonValue} annotation (or equivalent), build serializer
* based on that property
* </li>
*</ul>
*
* @since 2.0
*/
protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider prov,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
Class<?> raw = type.getRawClass();
// First: JsonSerializable?
if (JsonSerializable.class.isAssignableFrom(raw)) {
return SerializableSerializer.instance;
}
// Second: @JsonValue for any type
AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor();
if (valueAccessor != null) {
if (prov.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(valueAccessor.getMember(),
prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor);
return new JsonValueSerializer(valueAccessor, ser);
}
// No well-known annotations...
return null;
}
Method for checking if we can determine serializer to use based on set of
known primary types, checking for set of known base types (exact matches
having been compared against with findSerializerByLookup
).
This does not include "secondary" interfaces, but
mostly concrete or abstract base classes.
/**
* Method for checking if we can determine serializer to use based on set of
* known primary types, checking for set of known base types (exact matches
* having been compared against with <code>findSerializerByLookup</code>).
* This does not include "secondary" interfaces, but
* mostly concrete or abstract base classes.
*/
protected final JsonSerializer<?> findSerializerByPrimaryType(SerializerProvider prov,
JavaType type, BeanDescription beanDesc,
boolean staticTyping)
throws JsonMappingException
{
if (type.isEnumType()) {
return buildEnumSerializer(prov.getConfig(), type, beanDesc);
}
final Class<?> raw = type.getRawClass();
// Then check for optional/external serializers
JsonSerializer<?> ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping);
if (ser != null) {
return ser;
}
if (Calendar.class.isAssignableFrom(raw)) {
return CalendarSerializer.instance;
}
if (java.util.Date.class.isAssignableFrom(raw)) {
return DateSerializer.instance;
}
if (Map.Entry.class.isAssignableFrom(raw)) {
// 18-Oct-2015, tatu: With 2.7, need to dig type info:
JavaType mapEntryType = type.findSuperType(Map.Entry.class);
// 28-Apr-2015, tatu: TypeFactory does it all for us already so
JavaType kt = mapEntryType.containedTypeOrUnknown(0);
JavaType vt = mapEntryType.containedTypeOrUnknown(1);
return buildMapEntrySerializer(prov, type, beanDesc, staticTyping, kt, vt);
}
if (ByteBuffer.class.isAssignableFrom(raw)) {
return new ByteBufferSerializer();
}
if (InetAddress.class.isAssignableFrom(raw)) {
return new InetAddressSerializer();
}
if (InetSocketAddress.class.isAssignableFrom(raw)) {
return new InetSocketAddressSerializer();
}
if (TimeZone.class.isAssignableFrom(raw)) {
return new TimeZoneSerializer();
}
if (java.nio.charset.Charset.class.isAssignableFrom(raw)) {
return ToStringSerializer.instance;
}
if (Number.class.isAssignableFrom(raw)) {
// 21-May-2014, tatu: Couple of alternatives actually
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
if (format != null) {
switch (format.getShape()) {
case STRING:
return ToStringSerializer.instance;
case OBJECT: // need to bail out to let it be serialized as POJO
case ARRAY: // or, I guess ARRAY; otherwise no point in speculating
return null;
default:
}
}
return NumberSerializer.instance;
}
return null;
}
Overridable method called after checking all other types.
Since: 2.2
/**
* Overridable method called after checking all other types.
*
* @since 2.2
*/
protected JsonSerializer<?> findOptionalStdSerializer(SerializerProvider prov,
JavaType type, BeanDescription beanDesc, boolean staticTyping)
throws JsonMappingException
{
return OptionalHandlerFactory.instance.findSerializer(prov.getConfig(), type, beanDesc);
}
Reflection-based serialized find method, which checks if given class implements one of recognized "add-on" interfaces. Add-on here means a role that is usually or can be a secondary trait: for example, bean classes may implement Iterable
, but their main function is usually something else. The reason for /**
* Reflection-based serialized find method, which checks if
* given class implements one of recognized "add-on" interfaces.
* Add-on here means a role that is usually or can be a secondary
* trait: for example,
* bean classes may implement {@link Iterable}, but their main
* function is usually something else. The reason for
*/
protected final JsonSerializer<?> findSerializerByAddonType(SerializationConfig config,
JavaType javaType, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException
{
Class<?> rawType = javaType.getRawClass();
if (Iterator.class.isAssignableFrom(rawType)) {
JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterator.class);
JavaType vt = (params == null || params.length != 1) ?
TypeFactory.unknownType() : params[0];
return buildIteratorSerializer(config, javaType, beanDesc, staticTyping, vt);
}
if (Iterable.class.isAssignableFrom(rawType)) {
JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterable.class);
JavaType vt = (params == null || params.length != 1) ?
TypeFactory.unknownType() : params[0];
return buildIterableSerializer(config, javaType, beanDesc, staticTyping, vt);
}
if (CharSequence.class.isAssignableFrom(rawType)) {
return ToStringSerializer.instance;
}
return null;
}
Helper method called to check if a class or method
has an annotation
(@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using)
that tells the class to use for serialization.
Returns null if no such annotation found.
/**
* Helper method called to check if a class or method
* has an annotation
* (@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using)
* that tells the class to use for serialization.
* Returns null if no such annotation found.
*/
@SuppressWarnings("unchecked")
protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov,
Annotated a)
throws JsonMappingException
{
Object serDef = prov.getAnnotationIntrospector().findSerializer(a);
if (serDef == null) {
return null;
}
JsonSerializer<Object> ser = prov.serializerInstance(a, serDef);
// One more thing however: may need to also apply a converter:
return (JsonSerializer<Object>) findConvertingSerializer(prov, a, ser);
}
Helper method that will check whether given annotated entity (usually class, but may also be a property accessor) indicates that a Converter
is to be used; and if so, to construct and return suitable serializer for it. If not, will simply return given serializer as is. /**
* Helper method that will check whether given annotated entity (usually class,
* but may also be a property accessor) indicates that a {@link Converter} is to
* be used; and if so, to construct and return suitable serializer for it.
* If not, will simply return given serializer as is.
*/
protected JsonSerializer<?> findConvertingSerializer(SerializerProvider prov,
Annotated a, JsonSerializer<?> ser)
throws JsonMappingException
{
Converter<Object,Object> conv = findConverter(prov, a);
if (conv == null) {
return ser;
}
JavaType delegateType = conv.getOutputType(prov.getTypeFactory());
return new StdDelegatingSerializer(conv, delegateType, ser);
}
protected Converter<Object,Object> findConverter(SerializerProvider prov,
Annotated a)
throws JsonMappingException
{
Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(a);
if (convDef == null) {
return null;
}
return prov.converterInstance(a, convDef);
}
/*
/**********************************************************
/* Factory methods, container types:
/**********************************************************
*/
Since: 2.1
/**
* @since 2.1
*/
protected JsonSerializer<?> buildContainerSerializer(SerializerProvider prov,
JavaType type, BeanDescription beanDesc, boolean staticTyping)
throws JsonMappingException
{
final SerializationConfig config = prov.getConfig();
/* [databind#23], 15-Mar-2013, tatu: must force static handling of root value type,
* with just one important exception: if value type is "untyped", let's
* leave it as is; no clean way to make it work.
*/
if (!staticTyping && type.useStaticType()) {
if (!type.isContainerType() || !type.getContentType().isJavaLangObject()) {
staticTyping = true;
}
}
// Let's see what we can learn about element/content/value type, type serializer for it:
JavaType elementType = type.getContentType();
TypeSerializer elementTypeSerializer = createTypeSerializer(config,
elementType);
// if elements have type serializer, cannot force static typing:
if (elementTypeSerializer != null) {
staticTyping = false;
}
JsonSerializer<Object> elementValueSerializer = _findContentSerializer(prov,
beanDesc.getClassInfo());
if (type.isMapLikeType()) { // implements java.util.Map
MapLikeType mlt = (MapLikeType) type;
/* 29-Sep-2012, tatu: This is actually too early to (try to) find
* key serializer from property annotations, and can lead to caching
* issues (see [databind#75]). Instead, must be done from 'createContextual()' call.
* But we do need to check class annotations.
*/
JsonSerializer<Object> keySerializer = _findKeySerializer(prov, beanDesc.getClassInfo());
if (mlt.isTrueMapType()) {
return buildMapSerializer(prov, (MapType) mlt, beanDesc, staticTyping,
keySerializer, elementTypeSerializer, elementValueSerializer);
}
// With Map-like, just 2 options: (1) Custom, (2) Annotations
JsonSerializer<?> ser = null;
MapLikeType mlType = (MapLikeType) type;
for (Serializers serializers : customSerializers()) { // (1) Custom
ser = serializers.findMapLikeSerializer(config,
mlType, beanDesc, keySerializer, elementTypeSerializer, elementValueSerializer);
if (ser != null) {
break;
}
}
if (ser == null) { // (2) Annotations-based ones:
ser = findSerializerByAnnotations(prov, type, beanDesc);
}
if (ser != null) {
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyMapLikeSerializer(config, mlType, beanDesc, ser);
}
}
}
return ser;
}
if (type.isCollectionLikeType()) {
CollectionLikeType clt = (CollectionLikeType) type;
if (clt.isTrueCollectionType()) {
return buildCollectionSerializer(prov, (CollectionType) clt, beanDesc, staticTyping,
elementTypeSerializer, elementValueSerializer);
}
// With Map-like, just 2 options: (1) Custom, (2) Annotations
JsonSerializer<?> ser = null;
CollectionLikeType clType = (CollectionLikeType) type;
for (Serializers serializers : customSerializers()) { // (1) Custom
ser = serializers.findCollectionLikeSerializer(config,
clType, beanDesc, elementTypeSerializer, elementValueSerializer);
if (ser != null) {
break;
}
}
if (ser == null) { // (2) Annotations-based ones:
ser = findSerializerByAnnotations(prov, type, beanDesc);
}
if (ser != null) {
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyCollectionLikeSerializer(config, clType, beanDesc, ser);
}
}
}
return ser;
}
if (type.isArrayType()) {
return buildArraySerializer(prov, (ArrayType) type, beanDesc, staticTyping,
elementTypeSerializer, elementValueSerializer);
}
return null;
}
Helper method that handles configuration details when constructing serializers for List
types that support efficient by-index access Since: 2.1
/**
* Helper method that handles configuration details when constructing serializers for
* {@link java.util.List} types that support efficient by-index access
*
* @since 2.1
*/
protected JsonSerializer<?> buildCollectionSerializer(SerializerProvider prov,
CollectionType type, BeanDescription beanDesc, boolean staticTyping,
TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
throws JsonMappingException
{
SerializationConfig config = prov.getConfig();
JsonSerializer<?> ser = null;
// Order of lookups:
// 1. Custom serializers
// 2. Annotations (@JsonValue, @JsonDeserialize)
// 3. Defaults
for (Serializers serializers : customSerializers()) { // (1) Custom
ser = serializers.findCollectionSerializer(config,
type, beanDesc, elementTypeSerializer, elementValueSerializer);
if (ser != null) {
break;
}
}
if (ser == null) {
ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations
if (ser == null) {
// We may also want to use serialize Collections "as beans", if (and only if)
// this is specified with `@JsonFormat(shape=Object)`
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) {
return null;
}
Class<?> raw = type.getRawClass();
if (EnumSet.class.isAssignableFrom(raw)) {
// this may or may not be available (Class doesn't; type of field/method does)
JavaType enumType = type.getContentType();
// and even if nominally there is something, only use if it really is enum
if (!enumType.isEnumImplType()) { // usually since it's `Enum.class`
enumType = null;
}
ser = buildEnumSetSerializer(enumType);
} else {
Class<?> elementRaw = type.getContentType().getRawClass();
if (isIndexedList(raw)) {
if (elementRaw == String.class) {
// [JACKSON-829] Must NOT use if we have custom serializer
if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
ser = IndexedStringListSerializer.instance;
}
} else {
ser = buildIndexedListSerializer(type.getContentType(), staticTyping,
elementTypeSerializer, elementValueSerializer);
}
} else if (elementRaw == String.class) {
// [JACKSON-829] Must NOT use if we have custom serializer
if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
ser = StringCollectionSerializer.instance;
}
}
if (ser == null) {
ser = buildCollectionSerializer(type.getContentType(), staticTyping,
elementTypeSerializer, elementValueSerializer);
}
}
}
}
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyCollectionSerializer(config, type, beanDesc, ser);
}
}
return ser;
}
/*
/**********************************************************
/* Factory methods, for Collections
/**********************************************************
*/
protected boolean isIndexedList(Class<?> cls)
{
return RandomAccess.class.isAssignableFrom(cls);
}
public ContainerSerializer<?> buildIndexedListSerializer(JavaType elemType,
boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) {
return new IndexedListSerializer(elemType, staticTyping, vts, valueSerializer);
}
public ContainerSerializer<?> buildCollectionSerializer(JavaType elemType,
boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) {
return new CollectionSerializer(elemType, staticTyping, vts, valueSerializer);
}
public JsonSerializer<?> buildEnumSetSerializer(JavaType enumType) {
return new EnumSetSerializer(enumType);
}
/*
/**********************************************************
/* Factory methods, for Maps
/**********************************************************
*/
Helper method that handles configuration details when constructing serializers for Map
types. /**
* Helper method that handles configuration details when constructing serializers for
* {@link java.util.Map} types.
*/
protected JsonSerializer<?> buildMapSerializer(SerializerProvider prov,
MapType type, BeanDescription beanDesc,
boolean staticTyping, JsonSerializer<Object> keySerializer,
TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
throws JsonMappingException
{
// [databind#467]: This is where we could allow serialization "as POJO": But! It's
// nasty to undo, and does not apply on per-property basis. So, hardly optimal
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) {
return null;
}
JsonSerializer<?> ser = null;
// Order of lookups:
// 1. Custom serializers
// 2. Annotations (@JsonValue, @JsonDeserialize)
// 3. Defaults
final SerializationConfig config = prov.getConfig();
for (Serializers serializers : customSerializers()) { // (1) Custom
ser = serializers.findMapSerializer(config, type, beanDesc,
keySerializer, elementTypeSerializer, elementValueSerializer);
if (ser != null) { break; }
}
if (ser == null) {
ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations
if (ser == null) {
Object filterId = findFilterId(config, beanDesc);
// 01-May-2016, tatu: Which base type to use here gets tricky, since
// most often it ought to be `Map` or `EnumMap`, but due to abstract
// mapping it will more likely be concrete type like `HashMap`.
// So, for time being, just pass `Map.class`
JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Map.class,
beanDesc.getClassInfo());
Set<String> ignored = (ignorals == null) ? null
: ignorals.findIgnoredForSerialization();
MapSerializer mapSer = MapSerializer.construct(ignored,
type, staticTyping, elementTypeSerializer,
keySerializer, elementValueSerializer, filterId);
ser = _checkMapContentInclusion(prov, beanDesc, mapSer);
}
}
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyMapSerializer(config, type, beanDesc, ser);
}
}
return ser;
}
Helper method that does figures out content inclusion value to use, if any, and construct re-configured MapSerializer
appropriately. Since: 2.9
/**
* Helper method that does figures out content inclusion value to use, if any,
* and construct re-configured {@link MapSerializer} appropriately.
*
* @since 2.9
*/
@SuppressWarnings("deprecation")
protected MapSerializer _checkMapContentInclusion(SerializerProvider prov,
BeanDescription beanDesc, MapSerializer mapSer)
throws JsonMappingException
{
final JavaType contentType = mapSer.getContentType();
JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
contentType, Map.class);
// Need to support global legacy setting, for now:
JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
if (incl == JsonInclude.Include.USE_DEFAULTS
|| incl == JsonInclude.Include.ALWAYS) {
if (!prov.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
return mapSer.withContentInclusion(null, true);
}
return mapSer;
}
// NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor
// but code is not identical nor are these types related
Object valueToSuppress;
boolean suppressNulls = true; // almost always, but possibly not with CUSTOM
switch (incl) {
case NON_DEFAULT:
valueToSuppress = BeanUtil.getDefaultValue(contentType);
if (valueToSuppress != null) {
if (valueToSuppress.getClass().isArray()) {
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
}
}
break;
case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals
// and for referential types, also "empty", which in their case means "absent"
valueToSuppress = contentType.isReferenceType()
? MapSerializer.MARKER_FOR_EMPTY : null;
break;
case NON_EMPTY:
valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
break;
case CUSTOM: // new with 2.9
valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
if (valueToSuppress == null) { // is this legal?
suppressNulls = true;
} else {
suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
}
break;
case NON_NULL:
default: // should not matter but...
valueToSuppress = null;
break;
}
return mapSer.withContentInclusion(valueToSuppress, suppressNulls);
}
Since: 2.9
/**
* @since 2.9
*/
protected JsonSerializer<?> buildMapEntrySerializer(SerializerProvider prov,
JavaType type, BeanDescription beanDesc, boolean staticTyping,
JavaType keyType, JavaType valueType)
throws JsonMappingException
{
// [databind#865]: Allow serialization "as POJO" -- note: to undo, declare
// serialization as `Shape.NATURAL` instead; that's JSON Object too.
JsonFormat.Value formatOverride = prov.getDefaultPropertyFormat(Map.Entry.class);
JsonFormat.Value formatFromAnnotation = beanDesc.findExpectedFormat(null);
JsonFormat.Value format = JsonFormat.Value.merge(formatFromAnnotation, formatOverride);
if (format.getShape() == JsonFormat.Shape.OBJECT) {
return null;
}
MapEntrySerializer ser = new MapEntrySerializer(valueType, keyType, valueType,
staticTyping, createTypeSerializer(prov.getConfig(), valueType), null);
final JavaType contentType = ser.getContentType();
JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
contentType, Map.Entry.class);
// Need to support global legacy setting, for now:
JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
if (incl == JsonInclude.Include.USE_DEFAULTS
|| incl == JsonInclude.Include.ALWAYS) {
return ser;
}
// NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor
// but code is not identical nor are these types related
Object valueToSuppress;
boolean suppressNulls = true; // almost always, but possibly not with CUSTOM
switch (incl) {
case NON_DEFAULT:
valueToSuppress = BeanUtil.getDefaultValue(contentType);
if (valueToSuppress != null) {
if (valueToSuppress.getClass().isArray()) {
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
}
}
break;
case NON_ABSENT:
valueToSuppress = contentType.isReferenceType()
? MapSerializer.MARKER_FOR_EMPTY : null;
break;
case NON_EMPTY:
valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
break;
case CUSTOM:
valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
if (valueToSuppress == null) { // is this legal?
suppressNulls = true;
} else {
suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
}
break;
case NON_NULL:
default: // should not matter but...
valueToSuppress = null;
break;
}
return ser.withContentInclusion(valueToSuppress, suppressNulls);
}
Helper method used for finding inclusion definitions for structured
container types like Map
s and referential types
(like AtomicReference
).
Params: - contentType – Declared full content type of container
- configType – Raw base type under which `configOverride`, if any, needs to be defined
/**
* Helper method used for finding inclusion definitions for structured
* container types like <code>Map</code>s and referential types
* (like <code>AtomicReference</code>).
*
* @param contentType Declared full content type of container
* @param configType Raw base type under which `configOverride`, if any, needs to be defined
*/
protected JsonInclude.Value _findInclusionWithContent(SerializerProvider prov,
BeanDescription beanDesc,
JavaType contentType, Class<?> configType)
throws JsonMappingException
{
final SerializationConfig config = prov.getConfig();
// Defaulting gets complicated because we might have two distinct
// axis to consider: Container type itself , and then value (content) type.
// Start with Container-defaults, then use more-specific value override, if any.
// Start by getting global setting, overridden by Map-type-override
JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion());
inclV = config.getDefaultPropertyInclusion(configType, inclV);
// and then merge content-type overrides, if any. But note that there's
// content-to-value inclusion shift we have to do
JsonInclude.Value valueIncl = config.getDefaultPropertyInclusion(contentType.getRawClass(), null);
if (valueIncl != null) {
switch (valueIncl.getValueInclusion()) {
case USE_DEFAULTS:
break;
case CUSTOM:
inclV = inclV.withContentFilter(valueIncl.getContentFilter());
break;
default:
inclV = inclV.withContentInclusion(valueIncl.getValueInclusion());
}
}
return inclV;
}
/*
/**********************************************************
/* Factory methods, for Arrays
/**********************************************************
*/
Helper method that handles configuration details when constructing serializers for
Object[]
(and subtypes, except for String).
/**
* Helper method that handles configuration details when constructing serializers for
* <code>Object[]</code> (and subtypes, except for String).
*/
protected JsonSerializer<?> buildArraySerializer(SerializerProvider prov,
ArrayType type, BeanDescription beanDesc,
boolean staticTyping,
TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
throws JsonMappingException
{
// 25-Jun-2015, tatu: Note that unlike with Collection(Like) and Map(Like) types, array
// types cannot be annotated (in theory I guess we could have mix-ins but... ?)
// so we need not do primary annotation lookup here.
// So all we need is (1) Custom, (2) Default array serializers
SerializationConfig config = prov.getConfig();
JsonSerializer<?> ser = null;
for (Serializers serializers : customSerializers()) { // (1) Custom
ser = serializers.findArraySerializer(config,
type, beanDesc, elementTypeSerializer, elementValueSerializer);
if (ser != null) {
break;
}
}
if (ser == null) {
Class<?> raw = type.getRawClass();
// Important: do NOT use standard serializers if non-standard element value serializer specified
if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
if (String[].class == raw) {
ser = StringArraySerializer.instance;
} else {
// other standard types?
ser = StdArraySerializers.findStandardImpl(raw);
}
}
if (ser == null) {
ser = new ObjectArraySerializer(type.getContentType(), staticTyping, elementTypeSerializer,
elementValueSerializer);
}
}
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyArraySerializer(config, type, beanDesc, ser);
}
}
return ser;
}
/*
/**********************************************************
/* Factory methods for Reference types
/* (demoted from BeanSF down here in 2.9)
/**********************************************************
*/
Since: 2.7
/**
* @since 2.7
*/
public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType,
BeanDescription beanDesc, boolean staticTyping)
throws JsonMappingException
{
JavaType contentType = refType.getContentType();
TypeSerializer contentTypeSerializer = contentType.getTypeHandler();
final SerializationConfig config = prov.getConfig();
if (contentTypeSerializer == null) {
contentTypeSerializer = createTypeSerializer(config, contentType);
}
JsonSerializer<Object> contentSerializer = contentType.getValueHandler();
for (Serializers serializers : customSerializers()) {
JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc,
contentTypeSerializer, contentSerializer);
if (ser != null) {
return ser;
}
}
if (refType.isTypeOrSubTypeOf(AtomicReference.class)) {
return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping,
contentTypeSerializer, contentSerializer);
}
return null;
}
protected JsonSerializer<?> buildAtomicReferenceSerializer(SerializerProvider prov,
ReferenceType refType, BeanDescription beanDesc, boolean staticTyping,
TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentSerializer)
throws JsonMappingException
{
final JavaType contentType = refType.getReferencedType();
JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
contentType, AtomicReference.class);
// Need to support global legacy setting, for now:
JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
Object valueToSuppress;
boolean suppressNulls;
if (incl == JsonInclude.Include.USE_DEFAULTS
|| incl == JsonInclude.Include.ALWAYS) {
valueToSuppress = null;
suppressNulls = false;
} else {
suppressNulls = true;
switch (incl) {
case NON_DEFAULT:
valueToSuppress = BeanUtil.getDefaultValue(contentType);
if (valueToSuppress != null) {
if (valueToSuppress.getClass().isArray()) {
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
}
}
break;
case NON_ABSENT:
valueToSuppress = contentType.isReferenceType()
? MapSerializer.MARKER_FOR_EMPTY : null;
break;
case NON_EMPTY:
valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
break;
case CUSTOM:
valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
if (valueToSuppress == null) { // is this legal?
suppressNulls = true;
} else {
suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
}
break;
case NON_NULL:
default: // should not matter but...
valueToSuppress = null;
break;
}
}
AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping,
contentTypeSerializer, contentSerializer);
return ser.withContentInclusion(valueToSuppress, suppressNulls);
}
/*
/**********************************************************
/* Factory methods, for non-container types
/**********************************************************
*/
Since: 2.5
/**
* @since 2.5
*/
protected JsonSerializer<?> buildIteratorSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc, boolean staticTyping,
JavaType valueType)
throws JsonMappingException
{
return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType));
}
Since: 2.5
/**
* @since 2.5
*/
protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc, boolean staticTyping,
JavaType valueType)
throws JsonMappingException
{
return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType));
}
protected JsonSerializer<?> buildEnumSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
/* As per [databind#24], may want to use alternate shape, serialize as JSON Object.
* Challenge here is that EnumSerializer does not know how to produce
* POJO style serialization, so we must handle that special case separately;
* otherwise pass it to EnumSerializer.
*/
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) {
// one special case: suppress serialization of "getDeclaringClass()"...
((BasicBeanDescription) beanDesc).removeProperty("declaringClass");
// returning null will mean that eventually BeanSerializer gets constructed
return null;
}
@SuppressWarnings("unchecked")
Class<Enum<?>> enumClass = (Class<Enum<?>>) type.getRawClass();
JsonSerializer<?> ser = EnumSerializer.construct(enumClass, config, beanDesc, format);
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyEnumSerializer(config, type, beanDesc, ser);
}
}
return ser;
}
/*
/**********************************************************
/* Other helper methods
/**********************************************************
*/
Helper method called to try to find whether there is an annotation in the
class that indicates key serializer to use.
If so, will try to instantiate key serializer and return it; otherwise returns null.
/**
* Helper method called to try to find whether there is an annotation in the
* class that indicates key serializer to use.
* If so, will try to instantiate key serializer and return it; otherwise returns null.
*/
protected JsonSerializer<Object> _findKeySerializer(SerializerProvider prov,
Annotated a)
throws JsonMappingException
{
AnnotationIntrospector intr = prov.getAnnotationIntrospector();
Object serDef = intr.findKeySerializer(a);
if (serDef != null) {
return prov.serializerInstance(a, serDef);
}
return null;
}
Helper method called to try to find whether there is an annotation in the
class that indicates content ("value") serializer to use.
If so, will try to instantiate value serializer and return it; otherwise returns null.
/**
* Helper method called to try to find whether there is an annotation in the
* class that indicates content ("value") serializer to use.
* If so, will try to instantiate value serializer and return it; otherwise returns null.
*/
protected JsonSerializer<Object> _findContentSerializer(SerializerProvider prov,
Annotated a)
throws JsonMappingException
{
AnnotationIntrospector intr = prov.getAnnotationIntrospector();
Object serDef = intr.findContentSerializer(a);
if (serDef != null) {
return prov.serializerInstance(a, serDef);
}
return null;
}
Method called to find filter that is configured to be used with bean
serializer being built, if any.
/**
* Method called to find filter that is configured to be used with bean
* serializer being built, if any.
*/
protected Object findFilterId(SerializationConfig config, BeanDescription beanDesc) {
return config.getAnnotationIntrospector().findFilterId((Annotated)beanDesc.getClassInfo());
}
Helper method to check whether global settings and/or class
annotations for the bean class indicate that static typing
(declared types) should be used for properties.
(instead of dynamic runtime types).
Since: 2.1 (earlier had variant with additional 'property' parameter)
/**
* Helper method to check whether global settings and/or class
* annotations for the bean class indicate that static typing
* (declared types) should be used for properties.
* (instead of dynamic runtime types).
*
* @since 2.1 (earlier had variant with additional 'property' parameter)
*/
protected boolean usesStaticTyping(SerializationConfig config,
BeanDescription beanDesc, TypeSerializer typeSer)
{
/* 16-Aug-2010, tatu: If there is a (value) type serializer, we cannot force
* static typing; that would make it impossible to handle expected subtypes
*/
if (typeSer != null) {
return false;
}
AnnotationIntrospector intr = config.getAnnotationIntrospector();
JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo());
if (t != null && t != JsonSerialize.Typing.DEFAULT_TYPING) {
return (t == JsonSerialize.Typing.STATIC);
}
return config.isEnabled(MapperFeature.USE_STATIC_TYPING);
}
// Commented out in 2.9
/*
protected Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
{
if (src == null) {
return null;
}
if (!(src instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class<JsonSerializer> instead");
}
Class<?> cls = (Class<?>) src;
if (cls == noneClass || ClassUtil.isBogusClass(cls)) {
return null;
}
return cls;
}
*/
}