package com.fasterxml.jackson.databind.ser.impl;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.BeanUtil;
/**
* @since 2.5
*/
@SuppressWarnings("serial")
@JacksonStdImpl
public class MapEntrySerializer
extends ContainerSerializer<Map.Entry<?,?>>
implements ContextualSerializer
{
/**
* @since 2.9
*/
public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
/**
* Map-valued property being serialized with this instance
*/
protected final BeanProperty _property;
/**
* Whether static types should be used for serialization of values
* or not (if not, dynamic runtime type is used)
*/
protected final boolean _valueTypeIsStatic;
protected final JavaType _entryType, _keyType, _valueType;
/*
/**********************************************************
/* Serializers used
/**********************************************************
*/
/**
* Key serializer to use, if it can be statically determined
*/
protected JsonSerializer<Object> _keySerializer;
/**
* Value serializer to use, if it can be statically determined
*/
protected JsonSerializer<Object> _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.
*/
protected PropertySerializerMap _dynamicValueSerializers;
/*
/**********************************************************
/* Config settings, filtering
/**********************************************************
*/
/**
* Value that indicates suppression mechanism to use for <b>values contained</b>;
* either "filter" (of which <code>equals()</code> is called), or marker
* value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for
* non-null values.
* Note that inclusion value for Map instance itself is handled by caller (POJO
* property that refers to the Map value).
*
* @since 2.5
*/
protected final Object _suppressableValue;
/**
* Flag that indicates what to do with `null` values, distinct from
* handling of {@link #_suppressableValue}
*
* @since 2.9
*/
protected final boolean _suppressNulls;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
public MapEntrySerializer(JavaType type, JavaType keyType, JavaType valueType,
boolean staticTyping, TypeSerializer vts,
BeanProperty property)
{
super(type);
_entryType = type;
_keyType = keyType;
_valueType = valueType;
_valueTypeIsStatic = staticTyping;
_valueTypeSerializer = vts;
_property = property;
_dynamicValueSerializers = PropertySerializerMap.emptyForProperties();
_suppressableValue = null;
_suppressNulls = false;
}
@Deprecated // since 2.9
protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property,
TypeSerializer vts,
JsonSerializer<?> keySer, JsonSerializer<?> valueSer)
{
this(src, property, vts, keySer, valueSer,
src._suppressableValue, src._suppressNulls);
}
@SuppressWarnings("unchecked")
protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property,
TypeSerializer vts,
JsonSerializer<?> keySer, JsonSerializer<?> valueSer,
Object suppressableValue, boolean suppressNulls)
{
super(Map.class, false);
_entryType = src._entryType;
_keyType = src._keyType;
_valueType = src._valueType;
_valueTypeIsStatic = src._valueTypeIsStatic;
_valueTypeSerializer = src._valueTypeSerializer;
_keySerializer = (JsonSerializer<Object>) keySer;
_valueSerializer = (JsonSerializer<Object>) valueSer;
_dynamicValueSerializers = src._dynamicValueSerializers;
_property = src._property;
_suppressableValue = suppressableValue;
_suppressNulls = suppressNulls;
}
@Override
public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
return new MapEntrySerializer(this, _property, vts, _keySerializer, _valueSerializer,
_suppressableValue, _suppressNulls);
}
/**
* @since 2.9
*/
public MapEntrySerializer withResolved(BeanProperty property,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
Object suppressableValue, boolean suppressNulls) {
return new MapEntrySerializer(this, property, _valueTypeSerializer,
keySerializer, valueSerializer, suppressableValue, suppressNulls);
}
/**
* @since 2.9
*/
public MapEntrySerializer withContentInclusion(Object suppressableValue,
boolean suppressNulls) {
if ((_suppressableValue == suppressableValue)
&& (_suppressNulls == suppressNulls)) {
return this;
}
return new MapEntrySerializer(this, _property, _valueTypeSerializer,
_keySerializer, _valueSerializer, suppressableValue, suppressNulls);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider,
BeanProperty property) throws JsonMappingException
{
JsonSerializer<?> ser = null;
JsonSerializer<?> keySer = null;
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
// First: if we have a property, may have property-annotation overrides
if (propertyAcc != null && intr != null) {
Object serDef = intr.findKeySerializer(propertyAcc);
if (serDef != null) {
keySer = provider.serializerInstance(propertyAcc, serDef);
}
serDef = intr.findContentSerializer(propertyAcc);
if (serDef != null) {
ser = provider.serializerInstance(propertyAcc, serDef);
}
}
if (ser == null) {
ser = _valueSerializer;
}
// [databind#124]: May have a content converter
ser = findContextualConvertingSerializer(provider, property, ser);
if (ser == null) {
// 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
// we can consider it a static case as well.
// 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho
if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) {
ser = provider.findValueSerializer(_valueType, property);
}
}
if (keySer == null) {
keySer = _keySerializer;
}
if (keySer == null) {
keySer = provider.findKeySerializer(_keyType, property);
} else {
keySer = provider.handleSecondaryContextualization(keySer, property);
}
Object valueToSuppress = _suppressableValue;
boolean suppressNulls = _suppressNulls;
if (property != null) {
JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), null);
if (inclV != null) {
JsonInclude.Include incl = inclV.getContentInclusion();
if (incl != JsonInclude.Include.USE_DEFAULTS) {
switch (incl) {
case NON_DEFAULT:
valueToSuppress = BeanUtil.getDefaultValue(_valueType);
suppressNulls = true;
if (valueToSuppress != null) {
if (valueToSuppress.getClass().isArray()) {
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
}
}
break;
case NON_ABSENT:
suppressNulls = true;
valueToSuppress = _valueType.isReferenceType() ? MARKER_FOR_EMPTY : null;
break;
case NON_EMPTY:
suppressNulls = true;
valueToSuppress = MARKER_FOR_EMPTY;
break;
case CUSTOM:
valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
if (valueToSuppress == null) { // is this legal?
suppressNulls = true;
} else {
suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
}
break;
case NON_NULL:
valueToSuppress = null;
suppressNulls = true;
break;
case ALWAYS: // default
default:
valueToSuppress = null;
// 30-Sep-2016, tatu: Should not need to check global flags here,
// if inclusion forced to be ALWAYS
suppressNulls = false;
break;
}
}
}
}
MapEntrySerializer mser = withResolved(property, keySer, ser,
valueToSuppress, suppressNulls);
// but note: no (full) filtering or sorting (unlike Maps)
return mser;
}
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
@Override
public JavaType getContentType() {
return _valueType;
}
@Override
public JsonSerializer<?> getContentSerializer() {
return _valueSerializer;
}
@Override
public boolean hasSingleElement(Map.Entry<?,?> value) {
return true;
}
@Override
public boolean isEmpty(SerializerProvider prov, Entry<?, ?> entry)
{
Object value = entry.getValue();
if (value == null) {
return _suppressNulls;
}
if (_suppressableValue == null) {
return false;
}
JsonSerializer<Object> valueSer = _valueSerializer;
if (valueSer == null) {
// Let's not worry about generic types here, actually;
// unlikely to make any difference, but does add significant overhead
Class<?> cc = value.getClass();
valueSer = _dynamicValueSerializers.serializerFor(cc.getClass());
if (valueSer == null) {
try {
valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, prov);
} catch (JsonMappingException e) { // Ugh... can not just throw as-is, so...
return false;
}
}
}
if (_suppressableValue == MARKER_FOR_EMPTY) {
return valueSer.isEmpty(prov, value);
}
return _suppressableValue.equals(value);
}
/*
/**********************************************************
/* Serialization methods
/**********************************************************
*/
@Override
public void serialize(Map.Entry<?, ?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
gen.writeStartObject(value);
serializeDynamic(value, gen, provider);
gen.writeEndObject();
}
@Override
public void serializeWithType(Map.Entry<?, ?> value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException
{
typeSer.writeTypePrefixForObject(value, gen);
// [databind#631]: Assign current value, to be accessible by custom serializers
gen.setCurrentValue(value);
serializeDynamic(value, gen, provider);
typeSer.writeTypeSuffixForObject(value, gen);
}
protected void serializeDynamic(Map.Entry<?, ?> value, JsonGenerator gen,
SerializerProvider provider)
throws IOException
{
final TypeSerializer vts = _valueTypeSerializer;
final Object keyElem = value.getKey();
JsonSerializer<Object> keySerializer;
if (keyElem == null) {
keySerializer = provider.findNullKeySerializer(_keyType, _property);
} else {
keySerializer = _keySerializer;
}
// or by value; nulls often suppressed
final Object valueElem = value.getValue();
JsonSerializer<Object> valueSer;
// And then value
if (valueElem == null) {
if (_suppressNulls) {
return;
}
valueSer = provider.getDefaultNullValueSerializer();
} else {
valueSer = _valueSerializer;
if (valueSer == null) {
Class<?> cc = valueElem.getClass();
valueSer = _dynamicValueSerializers.serializerFor(cc);
if (valueSer == null) {
if (_valueType.hasGenericTypes()) {
valueSer = _findAndAddDynamic(_dynamicValueSerializers,
provider.constructSpecializedType(_valueType, cc), provider);
} else {
valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, provider);
}
}
}
// also may need to skip non-empty values:
if (_suppressableValue != null) {
if (_suppressableValue == MARKER_FOR_EMPTY) {
if (valueSer.isEmpty(provider, valueElem)) {
return;
}
} if (_suppressableValue.equals(valueElem)) {
return;
}
}
}
keySerializer.serialize(keyElem, gen, provider);
try {
if (vts == null) {
valueSer.serialize(valueElem, gen, provider);
} else {
valueSer.serializeWithType(valueElem, gen, provider, vts);
}
} catch (Exception e) {
String keyDesc = ""+keyElem;
wrapAndThrow(provider, e, value, keyDesc);
}
}
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
Class<?> type, SerializerProvider provider) throws JsonMappingException
{
PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
if (map != result.map) {
_dynamicValueSerializers = result.map;
}
return result.serializer;
}
protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
JavaType type, SerializerProvider provider) throws JsonMappingException
{
PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
if (map != result.map) {
_dynamicValueSerializers = result.map;
}
return result.serializer;
}
}