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

com.fasterxml.jackson.databind.SerializerProvider Maven / Gradle / Ivy

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

import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.cfg.DatatypeFeature;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.ser.impl.FailingSerializer;
import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap;
import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer;
import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer;
import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
import com.fasterxml.jackson.databind.ser.std.NullSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.TokenBuffer;

/**
 * Class that defines API used by {@link ObjectMapper} and
 * {@link JsonSerializer}s to obtain serializers capable of serializing
 * instances of specific types; as well as the default implementation
 * of the functionality.
 *

* Provider handles caching aspects of serializer handling; all construction * details are delegated to {@link SerializerFactory} instance. *

* Object life-cycle is such that an initial instance ("blueprint") is created * and referenced by {@link ObjectMapper} and {@link ObjectWriter} intances; * but for actual usage, a configured instance is created by using * a create method in sub-class * {@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}. * Only this instance can be used for actual serialization calls; blueprint * object is only to be used for creating instances. */ public abstract class SerializerProvider extends DatabindContext { /** * Setting for determining whether mappings for "unknown classes" should be * cached for faster resolution. Usually this isn't needed, but maybe it * is in some cases? */ protected final static boolean CACHE_UNKNOWN_MAPPINGS = false; public final static JsonSerializer DEFAULT_NULL_KEY_SERIALIZER = new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)"); /** * Placeholder serializer used when java.lang.Object typed property * is marked to be serialized. *
* NOTE: starting with 2.6, this instance is NOT used for any other types, and * separate instances are constructed for "empty" Beans. *

* NOTE: changed to protected for 2.3; no need to be publicly available. */ protected final static JsonSerializer DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer(); /* /********************************************************** /* Configuration, general /********************************************************** */ /** * Serialization configuration to use for serialization processing. */ protected final SerializationConfig _config; /** * View used for currently active serialization, if any. * Only set for non-blueprint instances. */ protected final Class _serializationView; /* /********************************************************** /* Configuration, factories /********************************************************** */ /** * Factory used for constructing actual serializer instances. * Only set for non-blueprint instances. */ protected final SerializerFactory _serializerFactory; /* /********************************************************** /* Helper objects for caching, reuse /********************************************************** */ /** * Cache for doing type-to-value-serializer lookups. */ protected final SerializerCache _serializerCache; /** * Lazily-constructed holder for per-call attributes. * Only set for non-blueprint instances. * * @since 2.3 */ protected transient ContextAttributes _attributes; /* /********************************************************** /* Configuration, specialized serializers /********************************************************** */ /** * Serializer that gets called for values of types for which no * serializers can be constructed. *

* The default serializer will simply thrown an exception. */ protected JsonSerializer _unknownTypeSerializer = DEFAULT_UNKNOWN_SERIALIZER; /** * Serializer used to output non-null keys of Maps (which will get * output as JSON Objects), if not null; if null, us the standard * default key serializer. */ protected JsonSerializer _keySerializer; /** * Serializer used to output a null value. Default implementation * writes nulls using {@link JsonGenerator#writeNull}. */ protected JsonSerializer _nullValueSerializer = NullSerializer.instance; /** * Serializer used to (try to) output a null key, due to an entry of * {@link java.util.Map} having null key. * The default implementation will throw an exception if this happens; * alternative implementation (like one that would write an Empty String) * can be defined. */ protected JsonSerializer _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER; /* /********************************************************** /* State, for non-blueprint instances: generic /********************************************************** */ /** * 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; /** * Flag set to indicate that we are using vanilla null value serialization * * @since 2.3 */ protected final boolean _stdNullValueSerializer; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * Constructor for creating master (or "blue-print") provider object, * which is only used as the template for constructing per-binding * instances. */ public SerializerProvider() { _config = null; _serializerFactory = null; _serializerCache = new SerializerCache(); // Blueprints doesn't have access to any serializers... _knownSerializers = null; _serializationView = null; _attributes = null; // not relevant for blueprint instance, could set either way: _stdNullValueSerializer = true; } /** * "Copy-constructor", used by sub-classes when creating actual non-blueprint * instances to use. * * @param src Blueprint object used as the baseline for this instance */ protected SerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) { _serializerFactory = f; _config = config; _serializerCache = src._serializerCache; _unknownTypeSerializer = src._unknownTypeSerializer; _keySerializer = src._keySerializer; _nullValueSerializer = src._nullValueSerializer; _nullKeySerializer = src._nullKeySerializer; _stdNullValueSerializer = (_nullValueSerializer == DEFAULT_NULL_KEY_SERIALIZER); _serializationView = 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(); } /** * Copy-constructor used when making a copy of a blueprint instance. * * @since 2.5 */ protected SerializerProvider(SerializerProvider src) { // since this is assumed to be a blue-print instance, many settings missing: _config = null; _serializationView = null; _serializerFactory = null; _knownSerializers = null; // and others initialized to default empty state _serializerCache = new SerializerCache(); _unknownTypeSerializer = src._unknownTypeSerializer; _keySerializer = src._keySerializer; _nullValueSerializer = src._nullValueSerializer; _nullKeySerializer = src._nullKeySerializer; _stdNullValueSerializer = src._stdNullValueSerializer; } /* /********************************************************** /* Methods for configuring default settings /********************************************************** */ /** * Method that can be used to specify serializer to use for serializing * all non-null JSON property names, unless more specific key serializer * is found (i.e. if not custom key serializer has been registered for * Java type). *

* Note that key serializer registration are different from value serializer * registrations. */ public void setDefaultKeySerializer(JsonSerializer ks) { if (ks == null) { throw new IllegalArgumentException("Cannot pass null JsonSerializer"); } _keySerializer = ks; } /** * Method that can be used to specify serializer that will be * used to write JSON values matching Java null values * instead of default one (which simply writes JSON null). *

* Note that you can get finer control over serializer to use by overriding * {@link #findNullValueSerializer}, which gets called once per each * property. */ public void setNullValueSerializer(JsonSerializer nvs) { if (nvs == null) { throw new IllegalArgumentException("Cannot pass null JsonSerializer"); } _nullValueSerializer = nvs; } /** * Method that can be used to specify serializer that will be * used to write JSON property names matching null keys for Java * Maps (which will otherwise throw an exception if try write such property name) */ public void setNullKeySerializer(JsonSerializer nks) { if (nks == null) { throw new IllegalArgumentException("Cannot pass null JsonSerializer"); } _nullKeySerializer = nks; } /* /********************************************************** /* 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 // since 2.11 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 _serializationView; } @Override public final boolean canOverrideAccessModifiers() { return _config.canOverrideAccessModifiers(); } @Override public final boolean isEnabled(MapperFeature feature) { return _config.isEnabled(feature); } @Override // @since 2.14 public final boolean isEnabled(DatatypeFeature feature) { return _config.isEnabled(feature); } @Override public final JsonFormat.Value getDefaultPropertyFormat(Class baseType) { return _config.getDefaultPropertyFormat(baseType); } /** * @since 2.8 */ public final JsonInclude.Value getDefaultPropertyInclusion(Class baseType) { return _config.getDefaultPropertyInclusion(baseType); } /** * Method for accessing default Locale to use: convenience method for *
     *   getConfig().getLocale();
     *
*/ @Override public Locale getLocale() { return _config.getLocale(); } /** * Method for accessing default TimeZone to use: convenience method for *
     *   getConfig().getTimeZone();
     *
*/ @Override public TimeZone getTimeZone() { return _config.getTimeZone(); } /* /********************************************************** /* Generic attributes (2.3+) /********************************************************** */ @Override public Object getAttribute(Object key) { return _attributes.getAttribute(key); } @Override public SerializerProvider setAttribute(Object key, Object value) { _attributes = _attributes.withPerCallAttribute(key, value); return this; } /* /********************************************************** /* Access to general configuration /********************************************************** */ /** * Convenience method for checking whether specified serialization * feature is enabled or not. * Shortcut for: *
     *  getConfig().isEnabled(feature);
     *
*/ public final boolean isEnabled(SerializationFeature feature) { return _config.isEnabled(feature); } /** * "Bulk" access method for checking that all features specified by * mask are enabled. * * @since 2.3 */ public final boolean hasSerializationFeatures(int featureMask) { return _config.hasSerializationFeatures(featureMask); } /** * Convenience method for accessing provider to find serialization filters used, * equivalent to calling: *
     *   getConfig().getFilterProvider();
     *
*/ public final FilterProvider getFilterProvider() { return _config.getFilterProvider(); } /** *

* NOTE: current implementation simply returns `null` as generator is not yet * assigned to this provider. * * @since 2.8 */ public JsonGenerator getGenerator() { return null; } /* /********************************************************************** /* 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. * * @since 2.13 */ public TokenBuffer bufferForValueConversion(ObjectCodec oc) { // false -> no native type/object ids return new TokenBuffer(oc, false); } /** * Specialized factory method used when we are converting values and do not * typically have or use "real" parsers or generators. * * @since 2.13 */ public final TokenBuffer bufferForValueConversion() { return bufferForValueConversion(null); } /* /********************************************************** /* 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); /* /********************************************************** /* General serializer locating functionality /********************************************************** */ /** * Method called to get hold of a serializer for a value of given type; * or if no such serializer can be found, a default handler (which * may do a best-effort generic serialization or just simply * throw an exception when invoked). *

* Note: this method is only called for non-null values; not for keys * or null values. For these, check out other accessor methods. *

* Note that serializers produced should NOT handle polymorphic serialization * aspects; separate {@link TypeSerializer} is to be constructed by caller * if and as necessary. * * @throws JsonMappingException if there are fatal problems with * accessing suitable serializer; including that of not * finding any serializer */ @SuppressWarnings("unchecked") public JsonSerializer findValueSerializer(Class valueType, BeanProperty property) throws JsonMappingException { // Fast lookup from local lookup thingy works? JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { // If not, maybe shared map already has it? ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { // ... possibly as fully typed? ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); if (ser == null) { // If neither, must create ser = _createAndCacheUntypedSerializer(valueType); // Not found? Must use the unknown type serializer, which will report error later on if (ser == null) { ser = getUnknownTypeSerializer(valueType); // Should this be added to lookups? if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } } // at this point, resolution has occured, but not contextualization return (JsonSerializer) handleSecondaryContextualization(ser, property); } /** * Similar to {@link #findValueSerializer(Class,BeanProperty)}, but takes * full generics-aware type instead of raw class. * This is necessary for accurate handling of external type information, * to handle polymorphic types. *

* Note: this call will also contextualize serializer before returning it. * * @param property When creating secondary serializers, property for which * serializer is needed: annotations of the property (or bean that contains it) * may be checked to create contextual serializers. */ @SuppressWarnings("unchecked") public JsonSerializer findValueSerializer(JavaType valueType, BeanProperty property) throws JsonMappingException { if (valueType == null) { reportMappingProblem("Null passed for `valueType` of `findValueSerializer()`"); } // (see comments from above method) JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType.getRawClass()); if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } return (JsonSerializer) handleSecondaryContextualization(ser, property); } /** * Method variant used when we do NOT want contextualization to happen; it will need * to be handled at a later point, but caller wants to be able to do that * as needed; sometimes to avoid infinite loops * * @since 2.5 */ public JsonSerializer findValueSerializer(Class valueType) throws JsonMappingException { // (see comments from above method) JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType); if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } } } } } return ser; } /** * Method variant used when we do NOT want contextualization to happen; it will need * to be handled at a later point, but caller wants to be able to do that * as needed; sometimes to avoid infinite loops * * @since 2.5 */ public JsonSerializer findValueSerializer(JavaType valueType) throws JsonMappingException { // (see comments from above method) JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType.getRawClass()); if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } } } } return ser; } /** * Similar to {@link #findValueSerializer(JavaType, BeanProperty)}, but used * when finding "primary" property value serializer (one directly handling * value of the property). Difference 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. * * @param valueType Type of values to serialize * @param property Property that is being handled; will never be null, and its * type has to match valueType parameter. * * @since 2.3 */ @SuppressWarnings("unchecked") public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, BeanProperty property) throws JsonMappingException { JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType.getRawClass()); // Should this be added to lookups? if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } return (JsonSerializer) handlePrimaryContextualization(ser, property); } /** * See {@link #findPrimaryPropertySerializer(JavaType, BeanProperty)} * * @since 2.3 */ @SuppressWarnings("unchecked") public JsonSerializer findPrimaryPropertySerializer(Class valueType, BeanProperty property) throws JsonMappingException { JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType); if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } } return (JsonSerializer) handlePrimaryContextualization(ser, property); } /** * Alternative to {@link #findPrimaryPropertySerializer(JavaType, BeanProperty)} called not * for primary value, but "content" of such primary serializer: element of an array or * {@link java.util.Collection}, value of {@link java.util.Map} entry and so on. * This means that {@code property} passed (if any) does NOT represent value for which * serializer is requested but its secondary type (or secondary type of that type, * recursively). *

* Serializer returned SHOULD NOT handle type information; caller will (have to) add * suitable wrapping if necessary. *

* Note: this call will also contextualize serializer (call {@code createContextual()} * before returning it, if applicable (implements {@code ContextualSerializer}) * * @param valueType Type of values to serialize * @param property Property that indirectly refers to value being serialized (optional, * may be {@code null} for root level serializers) * * @since 2.11 */ @SuppressWarnings("unchecked") public JsonSerializer findContentValueSerializer(JavaType valueType, BeanProperty property) throws JsonMappingException { JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType.getRawClass()); // Should this be added to lookups? if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } return (JsonSerializer) handleSecondaryContextualization(ser, property); } /** * See {@link #findContentValueSerializer(JavaType, BeanProperty)}. * * @since 2.11 */ @SuppressWarnings("unchecked") public JsonSerializer findContentValueSerializer(Class valueType, BeanProperty property) throws JsonMappingException { JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(valueType); if (ser == null) { ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); if (ser == null) { ser = _createAndCacheUntypedSerializer(valueType); if (ser == null) { ser = getUnknownTypeSerializer(valueType); if (CACHE_UNKNOWN_MAPPINGS) { _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); } return ser; } } } } return (JsonSerializer) handleSecondaryContextualization(ser, property); } /** * 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 currently only 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. * * @param valueType 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; this is just * a hint * @param property When creating secondary serializers, property for which * serializer is needed: annotations of the property (or bean that contains it) * may be checked to create contextual serializers. */ public JsonSerializer findTypedValueSerializer(Class valueType, boolean cache, BeanProperty property) throws JsonMappingException { // Two-phase lookups; local non-shared cache, then shared: JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); if (ser != null) { return ser; } // If not, maybe shared map already has it? ser = _serializerCache.typedValueSerializer(valueType); if (ser != null) { return ser; } // Well, let's just compose from pieces: ser = findValueSerializer(valueType, property); TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, _config.constructType(valueType)); if (typeSer != null) { typeSer = typeSer.forProperty(property); ser = new TypeWrappedSerializer(typeSer, ser); } if (cache) { _serializerCache.addTypedSerializer(valueType, 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 currently only 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. * * @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; this is just * a hint * @param property When creating secondary serializers, property for which * serializer is needed: annotations of the property (or bean that contains it) * may be checked to create contextual serializers. */ public JsonSerializer findTypedValueSerializer(JavaType valueType, boolean cache, BeanProperty property) throws JsonMappingException { // Two-phase lookups; local non-shared cache, then shared: JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); if (ser != null) { return ser; } // If not, maybe shared map already has it? ser = _serializerCache.typedValueSerializer(valueType); if (ser != null) { return ser; } // Well, let's just compose from pieces: ser = findValueSerializer(valueType, property); TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, valueType); if (typeSer != null) { typeSer = typeSer.forProperty(property); ser = new TypeWrappedSerializer(typeSer, ser); } if (cache) { _serializerCache.addTypedSerializer(valueType, ser); } return ser; } /** * 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 2.6 */ public TypeSerializer findTypeSerializer(JavaType javaType) throws JsonMappingException { return _serializerFactory.createTypeSerializer(_config, javaType); } /** * 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#writeFieldName}; * but also since behavior for some key types may differ. *

* Note that the serializer itself can be called with instances * of any Java object, but not nulls. */ public JsonSerializer findKeySerializer(JavaType keyType, BeanProperty property) throws JsonMappingException { JsonSerializer ser = _serializerFactory.createKeySerializer(this, keyType, _keySerializer); // 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too return _handleContextualResolvable(ser, property); } /** * @since 2.7 */ public JsonSerializer findKeySerializer(Class rawKeyType, BeanProperty property) throws JsonMappingException { return findKeySerializer(_config.constructType(rawKeyType), property); } /* /******************************************************** /* Accessors for specialized serializers /******************************************************** */ /** * @since 2.0 */ public JsonSerializer getDefaultNullKeySerializer() { return _nullKeySerializer; } /** * @since 2.0 */ public JsonSerializer getDefaultNullValueSerializer() { return _nullValueSerializer; } /** * Method called to get the serializer to use for serializing * Map keys that are nulls: this is needed since JSON does not allow * any non-String value as key, including null. *

* Typically, returned serializer * will either throw an exception, or use an empty String; but * other behaviors are possible. */ /** * 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. * * @since 2.0 */ public JsonSerializer findNullKeySerializer(JavaType serializationType, BeanProperty property) throws JsonMappingException { return _nullKeySerializer; } /** * Method called to get the serializer to use for serializing null * values for specified property. *

* 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. * * @since 2.0 */ public JsonSerializer findNullValueSerializer(BeanProperty property) throws JsonMappingException { 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. *

* Typically, returned serializer will throw an exception, * although alternatively {@link com.fasterxml.jackson.databind.ser.std.ToStringSerializer} * could be returned as well. * * @param unknownType Type for which no serializer is found */ public JsonSerializer getUnknownTypeSerializer(Class unknownType) { // 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class if (unknownType == Object.class) { return _unknownTypeSerializer; } // 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. * * @since 2.5 */ public boolean isUnknownTypeSerializer(JsonSerializer ser) { if ((ser == _unknownTypeSerializer) || (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.getClass() == UnknownSerializer.class) { return true; } } return false; } /* /********************************************************** /* 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, serialize will be properly resolved * (via {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}) and/or contextualized * (via {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}) as necessary. * * @param annotated Annotated entity that contained definition * @param serDef Serializer definition: either an instance or class */ public abstract JsonSerializer serializerInstance(Annotated annotated, Object serDef) throws JsonMappingException; /** * 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. * * @since 2.9 */ public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty, Class filterClass) throws JsonMappingException; /** * Follow-up method that may be called after calling {@link #includeFilterInstance}, * to check handling of `null` values by the filter. * * @since 2.9 */ public abstract boolean includeFilterSuppressNulls(Object filter) throws JsonMappingException; /* /********************************************************** /* Support for contextualization /********************************************************** */ /** * Method called for primary property serializers (ones * directly created to serialize values of a POJO property), * to handle details of resolving * {@link ContextualSerializer} with given property context. * * @param property Property for which the given primary serializer is used; never null. * * @since 2.3 */ public JsonSerializer handlePrimaryContextualization(JsonSerializer ser, BeanProperty property) throws JsonMappingException { if (ser != null) { if (ser instanceof ContextualSerializer) { ser = ((ContextualSerializer) ser).createContextual(this, property); } } return 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 resolving * {@link ContextualDeserializer} 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 * * @since 2.3 */ public JsonSerializer handleSecondaryContextualization(JsonSerializer ser, BeanProperty property) throws JsonMappingException { if (ser != null) { if (ser instanceof ContextualSerializer) { ser = ((ContextualSerializer) ser).createContextual(this, property); } } return ser; } /* /******************************************************** /* Convenience methods for serializing using default methods /******************************************************** */ /** * Convenience method that will serialize given value (which can be * null) using standard serializer locating functionality. It can * be called for all values including field and Map values, but usually * field values are best handled calling * {@link #defaultSerializeField} instead. */ public final void defaultSerializeValue(Object value, JsonGenerator gen) throws IOException { if (value == null) { if (_stdNullValueSerializer) { // minor perf optimization gen.writeNull(); } else { _nullValueSerializer.serialize(null, gen, this); } } else { Class cls = value.getClass(); findTypedValueSerializer(cls, true, null).serialize(value, gen, this); } } /** * Convenience method that will serialize given field with specified * value. Value may be null. Serializer is done using the usual * null) using standard serializer locating functionality. */ public final void defaultSerializeField(String fieldName, Object value, JsonGenerator gen) throws IOException { gen.writeFieldName(fieldName); if (value == null) { /* Note: can't easily check for suppression at this point * any more; caller must check it. */ if (_stdNullValueSerializer) { // minor perf optimization gen.writeNull(); } else { _nullValueSerializer.serialize(null, gen, this); } } else { Class cls = value.getClass(); findTypedValueSerializer(cls, true, null).serialize(value, gen, this); } } /** * 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 gen) throws IOException { if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { gen.writeNumber(timestamp); } else { gen.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 gen) throws IOException { if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { gen.writeNumber(date.getTime()); } else { gen.writeString(_dateFormat().format(date)); } } /** * Method that will handle serialization of Dates used as {@link java.util.Map} keys, * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS} * value (and if using textual representation, configured date format) */ public void defaultSerializeDateKey(long timestamp, JsonGenerator gen) throws IOException { if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { gen.writeFieldName(String.valueOf(timestamp)); } else { gen.writeFieldName(_dateFormat().format(new Date(timestamp))); } } /** * Method that will handle serialization of Dates used as {@link java.util.Map} keys, * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS} * value (and if using textual representation, configured date format) */ public void defaultSerializeDateKey(Date date, JsonGenerator gen) throws IOException { if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { gen.writeFieldName(String.valueOf(date.getTime())); } else { gen.writeFieldName(_dateFormat().format(date)); } } public final void defaultSerializeNull(JsonGenerator gen) throws IOException { if (_stdNullValueSerializer) { // minor perf optimization gen.writeNull(); } else { _nullValueSerializer.serialize(null, gen, this); } } /* /******************************************************** /* Error reporting /******************************************************** */ /** * Helper method called to indicate problem; default behavior is to construct and * throw a {@link JsonMappingException}, but in future may collect more than one * and only throw after certain number, or at the end of serialization. * * @since 2.8 */ public void reportMappingProblem(String message, Object... args) throws JsonMappingException { throw mappingException(message, args); } /** * 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 JsonMappingException}. * * @since 2.9 */ public T reportBadTypeDefinition(BeanDescription bean, String msg, Object... msgArgs) throws JsonMappingException { 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 JsonMappingException}. * * @since 2.9 */ public T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop, String message, Object... msgArgs) throws JsonMappingException { 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 reportBadDefinition(JavaType type, String msg) throws JsonMappingException { throw InvalidDefinitionException.from(getGenerator(), msg, type); } /** * @since 2.9 */ public T reportBadDefinition(JavaType type, String msg, Throwable cause) throws JsonMappingException { throw InvalidDefinitionException.from(getGenerator(), msg, type) .withCause(cause); } /** * @since 2.9 */ public T reportBadDefinition(Class raw, String msg, Throwable cause) throws JsonMappingException { throw InvalidDefinitionException.from(getGenerator(), msg, constructType(raw)) .withCause(cause); } /** * Helper method called to indicate problem; default behavior is to construct and * throw a {@link JsonMappingException}, but in future may collect more than one * and only throw after certain number, or at the end of serialization. * * @since 2.8 */ public void reportMappingProblem(Throwable t, String message, Object... msgArgs) throws JsonMappingException { message = _format(message, msgArgs); throw JsonMappingException.from(getGenerator(), message, t); } @Override public JsonMappingException 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); } /* /******************************************************** /* Error reporting, deprecated methods /******************************************************** */ /** * Factory method for constructing a {@link JsonMappingException}; * usually only indirectly used by calling * {@link #reportMappingProblem(String, Object...)}. * * @since 2.6 * * @deprecated Since 2.9 */ @Deprecated // since 2.9 public JsonMappingException mappingException(String message, Object... msgArgs) { return JsonMappingException.from(getGenerator(), _format(message, msgArgs)); } /** * Factory method for constructing a {@link JsonMappingException}; * usually only indirectly used by calling * {@link #reportMappingProblem(Throwable, String, Object...)} * * @since 2.8 * * @deprecated Since 2.9 */ @Deprecated // since 2.9 protected JsonMappingException mappingException(Throwable t, String message, Object... msgArgs) { return JsonMappingException.from(getGenerator(), _format(message, msgArgs), t); } /* /******************************************************** /* Helper methods /******************************************************** */ protected void _reportIncompatibleRootType(Object value, JavaType rootType) throws IOException { // 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))); } /** * Method that will try to find a serializer, either from cache * or by constructing one; but will not return an "unknown" serializer * if this cannot be done but rather returns null. * * @return Serializer if one can be found, null if not. */ protected JsonSerializer _findExplicitUntypedSerializer(Class runtimeType) throws JsonMappingException { // Fast lookup from local lookup thingy works? JsonSerializer ser = _knownSerializers.untypedValueSerializer(runtimeType); if (ser == null) { // If not, maybe shared map already has it? ser = _serializerCache.untypedValueSerializer(runtimeType); if (ser == null) { ser = _createAndCacheUntypedSerializer(runtimeType); } } /* 18-Sep-2014, tatu: This is unfortunate patch over related change * that pushes creation of "unknown type" serializer deeper down * in BeanSerializerFactory; as a result, we need to "undo" creation * here. */ if (isUnknownTypeSerializer(ser)) { return null; } return ser; } /* /********************************************************** /* 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 JsonSerializer _createAndCacheUntypedSerializer(Class rawType) throws JsonMappingException { JavaType fullType = _config.constructType(rawType); JsonSerializer ser; try { ser = _createUntypedSerializer(fullType); } catch (IllegalArgumentException iae) { // We better only expose checked exceptions, since those // are what caller is expected to handle reportBadDefinition(fullType, ClassUtil.exceptionMessage(iae)); ser = null; // doesn't matter but compiler whines otherwise } if (ser != null) { // 21-Dec-2015, tatu: Best to cache for both raw and full-type key _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this); } return ser; } protected JsonSerializer _createAndCacheUntypedSerializer(JavaType type) throws JsonMappingException { JsonSerializer ser; try { ser = _createUntypedSerializer(type); } catch (IllegalArgumentException iae) { // We better only expose checked exceptions, since those // are what caller is expected to handle ser = null; reportMappingProblem(iae, ClassUtil.exceptionMessage(iae)); } if (ser != null) { // 21-Dec-2015, tatu: Should we also cache using raw key? _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); } return ser; } /** * @since 2.1 */ protected JsonSerializer _createUntypedSerializer(JavaType type) throws JsonMappingException { /* 27-Mar-2015, tatu: Wish I knew exactly why/what, but [databind#738] * can be prevented by synchronizing on cache (not on 'this', however, * since there's one instance per serialization). * Perhaps not-yet-resolved instance might be exposed too early to callers. */ // 13-Apr-2018, tatu: Problem does NOT occur any more with late 2.8.x and 2.9.x // versions, likely due to concurrency fixes for `AnnotatedClass` introspection. // This sync block could probably be removed; but to minimize any risk of // regression sync block will only be removed from 3.0. // 23-Oct-2019, tatu: Due to continuation of 2.x line, removed from 2.11 // synchronized (_serializerCache) { return (JsonSerializer)_serializerFactory.createSerializer(this, type); // } } /** * Helper method called to resolve and contextualize given * serializer, if and as necessary. */ @SuppressWarnings("unchecked") protected JsonSerializer _handleContextualResolvable(JsonSerializer ser, BeanProperty property) throws JsonMappingException { if (ser instanceof ResolvableSerializer) { ((ResolvableSerializer) ser).resolve(this); } return (JsonSerializer) handleSecondaryContextualization(ser, property); } @SuppressWarnings("unchecked") protected JsonSerializer _handleResolvable(JsonSerializer ser) throws JsonMappingException { if (ser instanceof ResolvableSerializer) { ((ResolvableSerializer) ser).resolve(this); } return (JsonSerializer) ser; } /* /********************************************************** /* Internal methods /********************************************************** */ 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; } }