| package com.fasterxml.jackson.databind; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Type; |
| import java.text.DateFormat; |
| import java.util.Date; |
| |
| import com.fasterxml.jackson.core.*; |
| |
| import com.fasterxml.jackson.databind.annotation.NoClass; |
| import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; |
| import com.fasterxml.jackson.databind.cfg.SerializationConfig; |
| import com.fasterxml.jackson.databind.introspect.Annotated; |
| import com.fasterxml.jackson.databind.jsonschema.JsonSchema; |
| import com.fasterxml.jackson.databind.jsonschema.SchemaAware; |
| import com.fasterxml.jackson.databind.jsontype.TypeSerializer; |
| import com.fasterxml.jackson.databind.node.ObjectNode; |
| import com.fasterxml.jackson.databind.ser.*; |
| import com.fasterxml.jackson.databind.ser.impl.*; |
| import com.fasterxml.jackson.databind.ser.std.NullSerializer; |
| import com.fasterxml.jackson.databind.ser.std.StdKeySerializers; |
| import com.fasterxml.jackson.databind.type.TypeFactory; |
| import com.fasterxml.jackson.databind.util.ClassUtil; |
| import com.fasterxml.jackson.databind.util.RootNameLookup; |
| |
| /** |
| * 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. |
| *<p> |
| * Provider handles caching aspects of serializer handling; all construction |
| * details are delegated to {@link SerializerFactory} instance. |
| *<p> |
| * 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 |
| * {@link #createInstance}. |
| * Only this instance can be used for actual serialization calls; blueprint |
| * object is only to be used for creating instances. |
| */ |
| public abstract class SerializerProvider |
| { |
| protected final static JavaType TYPE_OBJECT = TypeFactory.defaultInstance().uncheckedSimpleType(Object.class); |
| |
| /** |
| * 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<Object> DEFAULT_NULL_KEY_SERIALIZER = |
| new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)"); |
| |
| public final static JsonSerializer<Object> 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. |
| */ |
| protected final Class<?> _serializationView; |
| |
| /* |
| /********************************************************** |
| /* Configuration, factories |
| /********************************************************** |
| */ |
| |
| /** |
| * Factory used for constructing actual serializer instances. |
| */ |
| final protected SerializerFactory _serializerFactory; |
| |
| /* |
| /********************************************************** |
| /* Configuration, caching |
| /********************************************************** |
| */ |
| |
| /** |
| * Cache for doing type-to-value-serializer lookups. |
| */ |
| final protected SerializerCache _serializerCache; |
| |
| /** |
| * Helper object for keeping track of introspected root names |
| */ |
| final protected RootNameLookup _rootNames; |
| |
| /* |
| /********************************************************** |
| /* Configuration, specialized serializers |
| /********************************************************** |
| */ |
| |
| /** |
| * Serializer that gets called for values of types for which no |
| * serializers can be constructed. |
| *<p> |
| * The default serializer will simply thrown an exception; a possible |
| * alternative that can be used would be |
| * {@link ToStringSerializer}. |
| */ |
| protected JsonSerializer<Object> _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<Object> _keySerializer; |
| |
| /** |
| * Serializer used to output a null value. Default implementation |
| * writes nulls using {@link JsonGenerator#writeNull}. |
| */ |
| protected JsonSerializer<Object> _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<Object> _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER; |
| |
| /* |
| /********************************************************** |
| /* State, for non-blueprint instances |
| /********************************************************** |
| */ |
| |
| /** |
| * 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; |
| |
| /* |
| /********************************************************** |
| /* 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; |
| _rootNames = new RootNameLookup(); |
| |
| _serializationView = null; |
| } |
| |
| /** |
| * "Copy-constructor", used from {@link #createInstance} (or by |
| * sub-classes) |
| * |
| * @param src Blueprint object used as the baseline for this instance |
| */ |
| protected SerializerProvider(SerializerProvider src, |
| SerializationConfig config, SerializerFactory f) |
| { |
| if (config == null) { |
| throw new NullPointerException(); |
| } |
| _serializerFactory = f; |
| _config = config; |
| |
| _serializerCache = src._serializerCache; |
| _unknownTypeSerializer = src._unknownTypeSerializer; |
| _keySerializer = src._keySerializer; |
| _nullValueSerializer = src._nullValueSerializer; |
| _nullKeySerializer = src._nullKeySerializer; |
| _rootNames = src._rootNames; |
| |
| /* Non-blueprint instances do have a read-only map; one that doesn't |
| * need synchronization for lookups. |
| */ |
| _knownSerializers = _serializerCache.getReadOnlyLookupMap(); |
| |
| _serializationView = config.getActiveView(); |
| } |
| |
| /** |
| * Overridable method, used to create a non-blueprint instances from the blueprint. |
| * This is needed to retain state during serialization. |
| */ |
| public abstract SerializerProvider createInstance(SerializationConfig config, |
| SerializerFactory jsf); |
| |
| /* |
| /********************************************************** |
| /* Methods for configuring default settings |
| /********************************************************** |
| */ |
| |
| /** |
| * 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 throw an exception if try write such property |
| * name) |
| */ |
| public void setDefaultKeySerializer(JsonSerializer<Object> ks) |
| { |
| if (ks == null) { |
| throw new IllegalArgumentException("Can not 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) |
| */ |
| public void setNullValueSerializer(JsonSerializer<Object> nvs) |
| { |
| if (nvs == null) { |
| throw new IllegalArgumentException("Can not pass null JsonSerializer"); |
| } |
| _nullValueSerializer = nvs; |
| } |
| |
| /** |
| * 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). |
| *<p> |
| * Note that key serializer registration are different from value serializer |
| * registrations. |
| */ |
| public void setNullKeySerializer(JsonSerializer<Object> nks) |
| { |
| if (nks == null) { |
| throw new IllegalArgumentException("Can not pass null JsonSerializer"); |
| } |
| _nullKeySerializer = nks; |
| } |
| |
| /* |
| /********************************************************** |
| /* Methods that ObjectMapper will call |
| /********************************************************** |
| */ |
| |
| /** |
| * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} |
| * for serializing given value, using serializers that |
| * this provider has access to (via caching and/or creating new serializers |
| * as need be). |
| */ |
| public void serializeValue(JsonGenerator jgen, Object value) |
| throws IOException, JsonGenerationException |
| { |
| JsonSerializer<Object> ser; |
| boolean wrap; |
| |
| if (value == null) { |
| // no type provided; must just use the default null serializer |
| ser = getDefaultNullValueSerializer(); |
| wrap = false; // no name to use for wrapping; can't do! |
| } else { |
| Class<?> cls = value.getClass(); |
| // true, since we do want to cache root-level typed serializers (ditto for null property) |
| ser = findTypedValueSerializer(cls, true, null); |
| |
| // Ok: should we wrap result in an additional property ("root name")? |
| String rootName = _config.getRootName(); |
| if (rootName == null) { // not explicitly specified |
| // [JACKSON-163] |
| wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); |
| if (wrap) { |
| jgen.writeStartObject(); |
| jgen.writeFieldName(_rootNames.findRootName(value.getClass(), _config)); |
| } |
| } else if (rootName.length() == 0) { |
| wrap = false; |
| } else { // [JACKSON-764] |
| // empty String means explicitly disabled; non-empty that it is enabled |
| wrap = true; |
| jgen.writeStartObject(); |
| jgen.writeFieldName(rootName); |
| } |
| } |
| try { |
| ser.serialize(value, jgen, this); |
| if (wrap) { |
| jgen.writeEndObject(); |
| } |
| } catch (IOException ioe) { |
| /* As per [JACKSON-99], should not wrap IOException or its |
| * sub-classes (like JsonProcessingException, JsonMappingException) |
| */ |
| throw ioe; |
| } catch (Exception e) { |
| // but others are wrapped |
| String msg = e.getMessage(); |
| if (msg == null) { |
| msg = "[no message for "+e.getClass().getName()+"]"; |
| } |
| throw new JsonMappingException(msg, e); |
| } |
| } |
| |
| /** |
| * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} |
| * for serializing given value (assumed to be of specified root type, |
| * instead of runtime type of value), |
| * using serializers that |
| * this provider has access to (via caching and/or creating new serializers |
| * as need be), |
| * |
| * @param rootType Type to use for locating serializer to use, instead of actual |
| * runtime type. Must be actual type, or one of its super types |
| */ |
| public void serializeValue(JsonGenerator jgen, Object value, |
| JavaType rootType) |
| throws IOException, JsonGenerationException |
| { |
| boolean wrap; |
| |
| JsonSerializer<Object> ser; |
| if (value == null) { |
| ser = getDefaultNullValueSerializer(); |
| wrap = false; |
| } else { |
| // Let's ensure types are compatible at this point |
| if (!rootType.getRawClass().isAssignableFrom(value.getClass())) { |
| _reportIncompatibleRootType(value, rootType); |
| } |
| // root value, not reached via property: |
| ser = findTypedValueSerializer(rootType, true, null); |
| // [JACKSON-163] |
| wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); |
| if (wrap) { |
| jgen.writeStartObject(); |
| jgen.writeFieldName(_rootNames.findRootName(rootType, _config)); |
| } |
| } |
| try { |
| ser.serialize(value, jgen, this); |
| if (wrap) { |
| jgen.writeEndObject(); |
| } |
| } catch (IOException ioe) { // no wrapping for IO (and derived) |
| throw ioe; |
| } catch (Exception e) { // but others do need to be, to get path etc |
| String msg = e.getMessage(); |
| if (msg == null) { |
| msg = "[no message for "+e.getClass().getName()+"]"; |
| } |
| throw new JsonMappingException(msg, e); |
| } |
| } |
| |
| /** |
| * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} |
| * to generate <a href="http://json-schema.org/">JSON schema</a> for |
| * given type. |
| * |
| * @param type The type for which to generate schema |
| */ |
| public JsonSchema generateJsonSchema(Class<?> type) |
| throws JsonMappingException |
| { |
| if (type == null) { |
| throw new IllegalArgumentException("A class must be provided"); |
| } |
| /* no need for embedded type information for JSON schema generation (all |
| * type information it needs is accessible via "untyped" serializer) |
| */ |
| JsonSerializer<Object> ser = findValueSerializer(type, null); |
| JsonNode schemaNode = (ser instanceof SchemaAware) ? |
| ((SchemaAware) ser).getSchema(this, null) : |
| JsonSchema.getDefaultSchemaNode(); |
| if (!(schemaNode instanceof ObjectNode)) { |
| throw new IllegalArgumentException("Class " + type.getName() + |
| " would not be serialized as a JSON object and therefore has no schema"); |
| } |
| return new JsonSchema((ObjectNode) schemaNode); |
| } |
| |
| /** |
| * Method that can be called to see if this serializer provider |
| * can find a serializer for an instance of given class. |
| *<p> |
| * Note that no Exceptions are thrown, including unchecked ones: |
| * implementations are to swallow exceptions if necessary. |
| */ |
| public boolean hasSerializerFor(Class<?> cls) { |
| return _findExplicitUntypedSerializer(cls, null) != null; |
| } |
| |
| /* |
| /********************************************************** |
| /* Access to configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Method for accessing configuration for the serialization processing. |
| */ |
| public final SerializationConfig getConfig() { return _config; } |
| |
| /** |
| * Convenience method for checking whether specified serialization |
| * feature is enabled or not. |
| * Shortcut for: |
| *<pre> |
| * getConfig().isEnabled(feature); |
| *</pre> |
| */ |
| public final boolean isEnabled(MapperFeature feature) { |
| return _config.isEnabled(feature); |
| } |
| |
| /** |
| * Convenience method for checking whether specified serialization |
| * feature is enabled or not. |
| * Shortcut for: |
| *<pre> |
| * getConfig().isEnabled(feature); |
| *</pre> |
| */ |
| public final boolean isEnabled(SerializationFeature feature) { |
| return _config.isEnabled(feature); |
| } |
| |
| /** |
| * Convenience method for accessing serialization view in use (if any); equivalent to: |
| *<pre> |
| * getConfig().canOverrideAccessModifiers(); |
| *</pre> |
| */ |
| public final boolean canOverrideAccessModifiers() { |
| return _config.canOverrideAccessModifiers(); |
| } |
| |
| /** |
| * Convenience method for accessing serialization view in use (if any); equivalent to: |
| *<pre> |
| * getConfig().getAnnotationIntrospector(); |
| *</pre> |
| */ |
| public final AnnotationIntrospector getAnnotationIntrospector() { |
| return _config.getAnnotationIntrospector(); |
| } |
| |
| /** |
| * Convenience method for accessing serialization view in use (if any); equivalent to: |
| *<pre> |
| * getConfig().getSerializationView(); |
| *</pre> |
| */ |
| public final Class<?> getSerializationView() { return _serializationView; } |
| |
| /** |
| * Convenience method for accessing provider to find serialization filters used, |
| * equivalent to calling: |
| *<pre> |
| * getConfig().getFilterProvider(); |
| *</pre> |
| */ |
| public final FilterProvider getFilterProvider() { |
| return _config.getFilterProvider(); |
| } |
| |
| /** |
| * Convenience method for constructing {@link JavaType} for given JDK |
| * type (usually {@link java.lang.Class}) |
| */ |
| public JavaType constructType(Type type) { |
| return _config.getTypeFactory().constructType(type); |
| } |
| |
| /** |
| * Convenience method for constructing subtypes, retaining generic |
| * type parameter (if any) |
| */ |
| public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) { |
| return _config.constructSpecializedType(baseType, subclass); |
| } |
| |
| /* |
| /********************************************************** |
| /* 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). |
| *<p> |
| * Note: this method is only called for non-null values; not for keys |
| * or null values. For these, check out other accessor methods. |
| *<p> |
| * Note that starting with version 1.5, serializers should also be type-aware |
| * if they handle polymorphic types. That means that it may be necessary |
| * to also use a {@link TypeSerializer} based on declared (static) type |
| * being serializer (whereas actual data may be serialized using dynamic |
| * type) |
| * |
| * @throws JsonMappingException if there are fatal problems with |
| * accessing suitable serializer; including that of not |
| * finding any serializer |
| */ |
| public JsonSerializer<Object> findValueSerializer(Class<?> valueType, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| // Fast lookup from local lookup thingy works? |
| JsonSerializer<Object> 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, property); |
| // Not found? Must use the unknown type serializer |
| /* Couldn't create? Need to return the fallback serializer, which |
| * most likely will report an error: but one question is whether |
| * we should cache it? |
| */ |
| 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 _handleContextual(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. |
| * |
| * @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<Object> findValueSerializer(JavaType valueType, BeanProperty property) |
| throws JsonMappingException |
| { |
| // Fast lookup from local lookup thingy works? |
| JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType); |
| if (ser == null) { |
| // If not, maybe shared map already has it? |
| ser = _serializerCache.untypedValueSerializer(valueType); |
| if (ser == null) { |
| // If neither, must create |
| ser = _createAndCacheUntypedSerializer(valueType, property); |
| // Not found? Must use the unknown type serializer |
| /* Couldn't create? Need to return the fallback serializer, which |
| * most likely will report an error: but one question is whether |
| * we should cache it? |
| */ |
| 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 _handleContextual(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<Object> findTypedValueSerializer(Class<?> valueType, |
| boolean cache, BeanProperty property) |
| throws JsonMappingException |
| { |
| // Two-phase lookups; local non-shared cache, then shared: |
| JsonSerializer<Object> 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<Object> findTypedValueSerializer(JavaType valueType, boolean cache, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| // Two-phase lookups; local non-shared cache, then shared: |
| JsonSerializer<Object> 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 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. |
| *<p> |
| * Note that the serializer itself can be called with instances |
| * of any Java object, but not nulls. |
| */ |
| public JsonSerializer<Object> findKeySerializer(JavaType keyType, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| JsonSerializer<Object> ser = _serializerFactory.createKeySerializer(_config, keyType); |
| |
| // First things first: maybe there are registered custom implementations |
| // if not, use default one: |
| if (ser == null) { |
| if (_keySerializer == null) { |
| ser = StdKeySerializers.getStdKeySerializer(keyType); |
| } else { |
| ser = _keySerializer; |
| } |
| } |
| // 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too |
| return _handleContextualResolvable(ser, property); |
| } |
| |
| /* |
| /******************************************************** |
| /* Accessors for specialized serializers |
| /******************************************************** |
| */ |
| |
| /** |
| * @since 2.0 |
| */ |
| public JsonSerializer<Object> getDefaultNullKeySerializer() { |
| return _nullKeySerializer; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public JsonSerializer<Object> 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. |
| *<p> |
| * 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 can not be |
| * determined. |
| * |
| * @since 2.0 |
| */ |
| public JsonSerializer<Object> findNullKeySerializer(JavaType serializationType, |
| BeanProperty property) |
| throws JsonMappingException { |
| return getDefaultNullKeySerializer(); |
| } |
| |
| /** |
| * Method called to get the serializer to use for serializing null |
| * property values. |
| *<p> |
| * Default implementation simply calls {@link #getDefaultNullValueSerializer()}; |
| * can be overridden to add custom null serialization for properties |
| * of certain type or name. |
| * |
| * @since 2.0 |
| */ |
| public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) |
| throws JsonMappingException { |
| return getDefaultNullValueSerializer(); |
| } |
| |
| /** |
| * Method called to get the serializer to use if provider |
| * can not determine an actual type-specific serializer |
| * to use; typically when none of {@link SerializerFactory} |
| * instances are able to construct a serializer. |
| *<p> |
| * Typically, returned serializer will throw an exception, |
| * although alternatively {@link com.fasterxml.jackson.databind.ser.std.ToStringSerializer} |
| * could be returned as well. |
| * |
| * @param unknownType Type for which no serializer is found |
| */ |
| public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) { |
| return _unknownTypeSerializer; |
| } |
| |
| /* |
| /********************************************************** |
| /* 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 JsonSerializer<Object> serializerInstance(Annotated annotated, |
| Object serDef) |
| throws JsonMappingException |
| { |
| if (serDef == null) { |
| return null; |
| } |
| JsonSerializer<?> ser; |
| |
| if (serDef instanceof JsonSerializer) { |
| ser = (JsonSerializer<?>) serDef; |
| } else { |
| /* Alas, there's no way to force return type of "either class |
| * X or Y" -- need to throw an exception after the fact |
| */ |
| if (!(serDef instanceof Class)) { |
| throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type " |
| +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead"); |
| } |
| Class<?> serClass = (Class<?>)serDef; |
| // there are some known "no class" markers to consider too: |
| if (serClass == JsonSerializer.None.class || serClass == NoClass.class) { |
| return null; |
| } |
| if (!JsonSerializer.class.isAssignableFrom(serClass)) { |
| throw new IllegalStateException("AnnotationIntrospector returned Class " |
| +serClass.getName()+"; expected Class<JsonSerializer>"); |
| } |
| HandlerInstantiator hi = _config.getHandlerInstantiator(); |
| if (hi != null) { |
| ser = hi.serializerInstance(_config, annotated, serClass); |
| } else { |
| ser = (JsonSerializer<?>) ClassUtil.createInstance(serClass, |
| _config.canOverrideAccessModifiers()); |
| } |
| } |
| return (JsonSerializer<Object>) _handleResolvable(ser); |
| } |
| |
| /* |
| /******************************************************** |
| /* Convenience 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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| if (value == null) { |
| getDefaultNullValueSerializer().serialize(null, jgen, this); |
| } else { |
| Class<?> cls = value.getClass(); |
| findTypedValueSerializer(cls, true, null).serialize(value, jgen, 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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| jgen.writeFieldName(fieldName); |
| if (value == null) { |
| /* Note: can't easily check for suppression at this point |
| * any more; caller must check it. |
| */ |
| getDefaultNullValueSerializer().serialize(null, jgen, this); |
| } else { |
| Class<?> cls = value.getClass(); |
| findTypedValueSerializer(cls, true, null).serialize(value, jgen, 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) |
| */ |
| /* |
| /********************************************************** |
| /* Abstract method impls, convenience methods |
| /********************************************************** |
| */ |
| |
| /** |
| * 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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| // [JACKSON-87]: Support both numeric timestamps and textual |
| if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { |
| jgen.writeNumber(timestamp); |
| } else { |
| if (_dateFormat == null) { |
| // must create a clone since Formats are not thread-safe: |
| _dateFormat = (DateFormat)_config.getDateFormat().clone(); |
| } |
| jgen.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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| // [JACKSON-87]: Support both numeric timestamps and textual |
| if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { |
| jgen.writeNumber(date.getTime()); |
| } else { |
| if (_dateFormat == null) { |
| DateFormat blueprint = _config.getDateFormat(); |
| // must create a clone since Formats are not thread-safe: |
| _dateFormat = (DateFormat)blueprint.clone(); |
| } |
| jgen.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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { |
| jgen.writeFieldName(String.valueOf(timestamp)); |
| } else { |
| if (_dateFormat == null) { |
| DateFormat blueprint = _config.getDateFormat(); |
| // must create a clone since Formats are not thread-safe: |
| _dateFormat = (DateFormat)blueprint.clone(); |
| } |
| jgen.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 jgen) |
| throws IOException, JsonProcessingException |
| { |
| if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { |
| jgen.writeFieldName(String.valueOf(date.getTime())); |
| } else { |
| if (_dateFormat == null) { |
| DateFormat blueprint = _config.getDateFormat(); |
| // must create a clone since Formats are not thread-safe: |
| _dateFormat = (DateFormat)blueprint.clone(); |
| } |
| jgen.writeFieldName(_dateFormat.format(date)); |
| } |
| } |
| |
| public final void defaultSerializeNull(JsonGenerator jgen) |
| throws IOException, JsonProcessingException |
| { |
| getDefaultNullValueSerializer().serialize(null, jgen, this); |
| } |
| |
| /* |
| /******************************************************** |
| /* Access to caching details |
| /******************************************************** |
| */ |
| |
| /** |
| * Method that can be used to determine how many serializers this |
| * provider is caching currently |
| * (if it does caching: default implementation does) |
| * Exact count depends on what kind of serializers get cached; |
| * default implementation caches all serializers, including ones that |
| * are eagerly constructed (for optimal access speed) |
| *<p> |
| * The main use case for this method is to allow conditional flushing of |
| * serializer cache, if certain number of entries is reached. |
| */ |
| public int cachedSerializersCount() { |
| return _serializerCache.size(); |
| } |
| |
| /** |
| * Method that will drop all serializers currently cached by this provider. |
| * This can be used to remove memory usage (in case some serializers are |
| * only used once or so), or to force re-construction of serializers after |
| * configuration changes for mapper than owns the provider. |
| */ |
| public void flushCachedSerializers() { |
| _serializerCache.flush(); |
| } |
| |
| protected void _reportIncompatibleRootType(Object value, JavaType rootType) |
| throws IOException, JsonProcessingException |
| { |
| /* 07-Jan-2010, tatu: As per [JACKSON-456] better handle distinction between wrapper types, |
| * primitives |
| */ |
| 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; |
| } |
| } |
| throw new JsonMappingException("Incompatible types: declared root type ("+rootType+") vs " |
| +value.getClass().getName()); |
| } |
| |
| /** |
| * Method that will try to find a serializer, either from cache |
| * or by constructing one; but will not return an "unknown" serializer |
| * if this can not be done but rather returns null. |
| * |
| * @return Serializer if one can be found, null if not. |
| */ |
| protected JsonSerializer<Object> _findExplicitUntypedSerializer(Class<?> runtimeType, |
| BeanProperty property) |
| { |
| // Fast lookup from local lookup thingy works? |
| JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(runtimeType); |
| if (ser != null) { |
| return ser; |
| } |
| // If not, maybe shared map already has it? |
| ser = _serializerCache.untypedValueSerializer(runtimeType); |
| if (ser != null) { |
| return ser; |
| } |
| try { |
| return _createAndCacheUntypedSerializer(runtimeType, property); |
| } catch (Exception e) { |
| return null; |
| } |
| } |
| |
| /* |
| /********************************************************** |
| /* 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<Object> _createAndCacheUntypedSerializer(Class<?> type, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| JsonSerializer<Object> ser; |
| try { |
| ser = _createUntypedSerializer(_config.constructType(type), property); |
| } catch (IllegalArgumentException iae) { |
| /* We better only expose checked exceptions, since those |
| * are what caller is expected to handle |
| */ |
| throw new JsonMappingException(iae.getMessage(), null, iae); |
| } |
| |
| if (ser != null) { |
| _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); |
| } |
| return ser; |
| } |
| |
| protected JsonSerializer<Object> _createAndCacheUntypedSerializer(JavaType type, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| JsonSerializer<Object> ser; |
| try { |
| ser = _createUntypedSerializer(type, property); |
| } catch (IllegalArgumentException iae) { |
| /* We better only expose checked exceptions, since those |
| * are what caller is expected to handle |
| */ |
| throw new JsonMappingException(iae.getMessage(), null, iae); |
| } |
| |
| if (ser != null) { |
| _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); |
| } |
| return ser; |
| } |
| |
| protected JsonSerializer<Object> _createUntypedSerializer(JavaType type, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| /* 10-Dec-2008, tatu: Is there a possibility of infinite loops |
| * here? Shouldn't be, given that we do not pass back-reference |
| * to this provider. But if there is, we'd need to sync calls, |
| * and keep track of creation chain to look for loops -- fairly |
| * easy to do, but won't add yet since it seems unnecessary. |
| */ |
| return (JsonSerializer<Object>)_serializerFactory.createSerializer(this, type, property); |
| } |
| |
| /** |
| * Helper method called to resolve and contextualize given |
| * serializer, if and as necessary. |
| */ |
| protected JsonSerializer<Object> _handleContextualResolvable(JsonSerializer<?> ser, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| if (ser instanceof ResolvableSerializer) { |
| ((ResolvableSerializer) ser).resolve(this); |
| } |
| return _handleContextual(ser, property); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected JsonSerializer<Object> _handleResolvable(JsonSerializer<?> ser) |
| throws JsonMappingException |
| { |
| if (ser instanceof ResolvableSerializer) { |
| ((ResolvableSerializer) ser).resolve(this); |
| } |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected JsonSerializer<Object> _handleContextual(JsonSerializer<?> ser, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| if (ser instanceof ContextualSerializer) { |
| ser = ((ContextualSerializer) ser).createContextual(this, property); |
| } |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| /* |
| /********************************************************** |
| /* Helper classes |
| /********************************************************** |
| */ |
| |
| /** |
| * Standard implementation used by {@link ObjectMapper}; just implements |
| * <code>createInstance</code> method which is abstract in |
| * {@link SerializerProvider} |
| */ |
| public final static class Impl extends SerializerProvider |
| { |
| public Impl() { super(); } |
| private Impl( SerializerProvider src, |
| SerializationConfig config,SerializerFactory f) { |
| super(src, config, f); |
| } |
| |
| @Override |
| public Impl createInstance(SerializationConfig config, SerializerFactory jsf) { |
| return new Impl(this, config, jsf); |
| } |
| |
| } |
| } |