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

org.codehaus.jackson.map.ser.MapSerializer Maven / Gradle / Ivy

Go to download

Data Mapper package is a high-performance data binding package built on Jackson JSON processor

There is a newer version: 1.9.13
Show newest version
package org.codehaus.jackson.map.ser;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
import org.codehaus.jackson.map.ser.impl.PropertySerializerMap;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.type.JavaType;

/**
 * Standard serializer implementation for serializing {link java.util.Map} types.
 *

* Note: about the only configurable setting currently is ability to filter out * entries with specified names. */ @JacksonStdImpl public class MapSerializer extends ContainerSerializerBase> implements ResolvableSerializer { protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType(); /** * Map-valued property being serialized with this instance * * @since 1.7 */ protected final BeanProperty _property; /** * Set of entries to omit during serialization, if any */ protected final HashSet _ignoredEntries; /** * Whether static types should be used for serialization of values * or not (if not, dynamic runtime type is used) */ protected final boolean _valueTypeIsStatic; /** * Declared type of keys * * @since 1.7 */ protected final JavaType _keyType; /** * Declared type of contained values */ protected final JavaType _valueType; /** * Key serializer to use, if it can be statically determined * * @since 1.7 */ protected JsonSerializer _keySerializer; /** * Value serializer to use, if it can be statically determined * * @since 1.5 */ protected JsonSerializer _valueSerializer; /** * Type identifier serializer used for values, if any. */ protected final TypeSerializer _valueTypeSerializer; /** * If value type can not be statically determined, mapping from * runtime value types to serializers are stored in this object. * * @since 1.8 */ protected PropertySerializerMap _dynamicValueSerializers; protected MapSerializer() { this((HashSet)null, null, null, false, null, null, null, null); } /** * Legacy constructor (as of 1.7) * * @deprecated Use variant that takes Key type and property information */ @Deprecated protected MapSerializer(HashSet ignoredEntries, JavaType valueType, boolean valueTypeIsStatic, TypeSerializer vts) { this(ignoredEntries, UNSPECIFIED_TYPE, valueType, valueTypeIsStatic, vts, null, null, null); } /** * Legacy constructor (as of 1.8) * * @deprecated As of 1.8, use version that takes valueSerializer */ @Deprecated protected MapSerializer(HashSet ignoredEntries, JavaType keyType, JavaType valueType, boolean valueTypeIsStatic, TypeSerializer vts, JsonSerializer keySerializer, BeanProperty property) { this(ignoredEntries, keyType, valueType, valueTypeIsStatic, vts, keySerializer, null, property); } protected MapSerializer(HashSet ignoredEntries, JavaType keyType, JavaType valueType, boolean valueTypeIsStatic, TypeSerializer vts, JsonSerializer keySerializer, JsonSerializer valueSerializer, BeanProperty property) { super(Map.class, false); _property = property; _ignoredEntries = ignoredEntries; _keyType = keyType; _valueType = valueType; _valueTypeIsStatic = valueTypeIsStatic; _valueTypeSerializer = vts; _keySerializer = keySerializer; _valueSerializer = valueSerializer; _dynamicValueSerializers = PropertySerializerMap.emptyMap(); } @Override public ContainerSerializerBase _withValueTypeSerializer(TypeSerializer vts) { MapSerializer ms = new MapSerializer(_ignoredEntries, _keyType, _valueType, _valueTypeIsStatic, vts, _keySerializer, _valueSerializer, _property); if (_valueSerializer != null) { ms._valueSerializer = _valueSerializer; } return ms; } /** * Factory method used to construct Map serializers. * * @param ignoredList Array of entry names that are to be filtered on * serialization; null if none * @param mapType Declared type information (needed for static typing) * @param staticValueType Whether static typing should be used for the * Map (which includes its contents) * @param vts Type serializer to use for map entry values, if any * * @deprecated As of 1.8; use the variant with more arguments */ @Deprecated public static MapSerializer construct(String[] ignoredList, JavaType mapType, boolean staticValueType, TypeSerializer vts, BeanProperty property) { return construct(ignoredList, mapType, staticValueType, vts, property, null, null); } public static MapSerializer construct(String[] ignoredList, JavaType mapType, boolean staticValueType, TypeSerializer vts, BeanProperty property, JsonSerializer keySerializer, JsonSerializer valueSerializer) { HashSet ignoredEntries = toSet(ignoredList); JavaType keyType, valueType; if (mapType == null) { keyType = valueType = UNSPECIFIED_TYPE; } else { keyType = mapType.getKeyType(); valueType = mapType.getContentType(); } // If value type is final, it's same as forcing static value typing: if (!staticValueType) { staticValueType = (valueType != null && valueType.isFinal()); } return new MapSerializer(ignoredEntries, keyType, valueType, staticValueType, vts, keySerializer, valueSerializer, property); } private static HashSet toSet(String[] ignoredEntries) { if (ignoredEntries == null || ignoredEntries.length == 0) { return null; } HashSet result = new HashSet(ignoredEntries.length); for (String prop : ignoredEntries) { result.add(prop); } return result; } /* /********************************************************** /* JsonSerializer implementation /********************************************************** */ @Override public void serialize(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeStartObject(); if (!value.isEmpty()) { if (_valueSerializer != null) { serializeFieldsUsing(value, jgen, provider, _valueSerializer); } else { serializeFields(value, jgen, provider); } } jgen.writeEndObject(); } @Override public void serializeWithType(Map value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException { typeSer.writeTypePrefixForObject(value, jgen); if (!value.isEmpty()) { if (_valueSerializer != null) { serializeFieldsUsing(value, jgen, provider, _valueSerializer); } else { serializeFields(value, jgen, provider); } } typeSer.writeTypeSuffixForObject(value, jgen); } /* /********************************************************** /* JsonSerializer implementation /********************************************************** */ /** * Method called to serialize fields, when the value type is not statically known. */ protected void serializeFields(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { // If value type needs polymorphic type handling, some more work needed: if (_valueTypeSerializer != null) { serializeTypedFields(value, jgen, provider); return; } final JsonSerializer keySerializer = _keySerializer; final HashSet ignored = _ignoredEntries; final boolean skipNulls = !provider.isEnabled(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES); PropertySerializerMap serializers = _dynamicValueSerializers; for (Map.Entry entry : value.entrySet()) { Object valueElem = entry.getValue(); // First, serialize key Object keyElem = entry.getKey(); if (keyElem == null) { provider.getNullKeySerializer().serialize(null, jgen, provider); } else { // [JACKSON-314] skip entries with null values? if (skipNulls && valueElem == null) continue; // One twist: is entry ignorable? If so, skip if (ignored != null && ignored.contains(keyElem)) continue; keySerializer.serialize(keyElem, jgen, provider); } // And then value if (valueElem == null) { provider.defaultSerializeNull(jgen); } else { Class cc = valueElem.getClass(); JsonSerializer serializer = serializers.serializerFor(cc); if (serializer == null) { if (_valueType.hasGenericTypes()) { serializer = _findAndAddDynamic(serializers, _valueType.forcedNarrowBy(cc), provider); } else { serializer = _findAndAddDynamic(serializers, cc, provider); } serializers = _dynamicValueSerializers; } try { serializer.serialize(valueElem, jgen, provider); } catch (Exception e) { // [JACKSON-55] Need to add reference information String keyDesc = ""+keyElem; wrapAndThrow(provider, e, value, keyDesc); } } } } /** * Method called to serialize fields, when the value type is statically known, * so that value serializer is passed and does not need to be fetched from * provider. */ protected void serializeFieldsUsing(Map value, JsonGenerator jgen, SerializerProvider provider, JsonSerializer ser) throws IOException, JsonGenerationException { final JsonSerializer keySerializer = _keySerializer; final HashSet ignored = _ignoredEntries; final TypeSerializer typeSer = _valueTypeSerializer; final boolean skipNulls = !provider.isEnabled(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES); for (Map.Entry entry : value.entrySet()) { Object valueElem = entry.getValue(); Object keyElem = entry.getKey(); if (keyElem == null) { provider.getNullKeySerializer().serialize(null, jgen, provider); } else { // [JACKSON-314] also may need to skip entries with null values if (skipNulls && valueElem == null) continue; if (ignored != null && ignored.contains(keyElem)) continue; keySerializer.serialize(keyElem, jgen, provider); } if (valueElem == null) { provider.defaultSerializeNull(jgen); } else { try { if (typeSer == null) { ser.serialize(valueElem, jgen, provider); } else { ser.serializeWithType(valueElem, jgen, provider, typeSer); } } catch (Exception e) { // [JACKSON-55] Need to add reference information String keyDesc = ""+keyElem; wrapAndThrow(provider, e, value, keyDesc); } } } } protected void serializeTypedFields(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { final JsonSerializer keySerializer = _keySerializer; JsonSerializer prevValueSerializer = null; Class prevValueClass = null; final HashSet ignored = _ignoredEntries; final boolean skipNulls = !provider.isEnabled(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES); for (Map.Entry entry : value.entrySet()) { Object valueElem = entry.getValue(); // First, serialize key Object keyElem = entry.getKey(); if (keyElem == null) { provider.getNullKeySerializer().serialize(null, jgen, provider); } else { // [JACKSON-314] also may need to skip entries with null values if (skipNulls && valueElem == null) continue; // One twist: is entry ignorable? If so, skip if (ignored != null && ignored.contains(keyElem)) continue; keySerializer.serialize(keyElem, jgen, provider); } // And then value if (valueElem == null) { provider.defaultSerializeNull(jgen); } else { Class cc = valueElem.getClass(); JsonSerializer currSerializer; if (cc == prevValueClass) { currSerializer = prevValueSerializer; } else { currSerializer = provider.findValueSerializer(cc, _property); prevValueSerializer = currSerializer; prevValueClass = cc; } try { currSerializer.serializeWithType(valueElem, jgen, provider, _valueTypeSerializer); } catch (Exception e) { // [JACKSON-55] Need to add reference information String keyDesc = ""+keyElem; wrapAndThrow(provider, e, value, keyDesc); } } } } @Override public JsonNode getSchema(SerializerProvider provider, Type typeHint) { ObjectNode o = createSchemaNode("object", true); //(ryan) even though it's possible to statically determine the "value" type of the map, // there's no way to statically determine the keys, so the "Entries" can't be determined. return o; } /** * Need to get callback to resolve value serializer, if static typing * is used (either being forced, or because value type is final) */ public void resolve(SerializerProvider provider) throws JsonMappingException { if (_valueTypeIsStatic && _valueSerializer == null) { _valueSerializer = provider.findValueSerializer(_valueType, _property); } /* 10-Dec-2010, tatu: Let's also fetch key serializer; and always assume we'll * do that just by using static type information */ /* 25-Feb-2011, tatu: May need to reconsider this static checking (since it * differs from value handling)... but for now, it's ok to ensure contextual * aspects are handled; this is done by provider. */ if (_keySerializer == null) { _keySerializer = provider.findKeySerializer(_keyType, _property); } } /* /********************************************************** /* Internal methods /********************************************************** */ protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, Class type, SerializerProvider provider) throws JsonMappingException { PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property); // did we get a new map of serializers? If so, start using it if (map != result.map) { _dynamicValueSerializers = result.map; } return result.serializer; } protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, JavaType type, SerializerProvider provider) throws JsonMappingException { PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property); if (map != result.map) { _dynamicValueSerializers = result.map; } return result.serializer; } }