blob: c29d5f32899f2b31476966efe3207a3e6aa13ece [file] [log] [blame]
package org.codehaus.jackson.map.ser.std;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
import org.codehaus.jackson.map.util.EnumValues;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.schema.JsonSchema;
import org.codehaus.jackson.schema.SchemaAware;
/**
* Specialized serializer for {@link EnumMap}s. Somewhat tricky to
* implement because actual Enum value type may not be available;
* and if not, it can only be gotten from actual instance.
*/
@JacksonStdImpl
public class EnumMapSerializer
extends ContainerSerializerBase<EnumMap<? extends Enum<?>, ?>>
implements ResolvableSerializer
{
protected final boolean _staticTyping;
/**
* If we know enumeration used as key, this will contain
* value set to use for serialization
*/
protected final EnumValues _keyEnums;
protected final JavaType _valueType;
/**
* Property being serialized with this instance
*
* @since 1.7
*/
protected final BeanProperty _property;
/**
* Value serializer to use, if it can be statically determined
*
* @since 1.5
*/
protected JsonSerializer<Object> _valueSerializer;
/**
* Type serializer used for values, if any.
*/
protected final TypeSerializer _valueTypeSerializer;
/**
* @deprecated Since 1.8, use variant that takes value serializer
*/
@Deprecated
public EnumMapSerializer(JavaType valueType, boolean staticTyping, EnumValues keyEnums,
TypeSerializer vts, BeanProperty property)
{
this(valueType, staticTyping, keyEnums, vts, property, null);
}
public EnumMapSerializer(JavaType valueType, boolean staticTyping, EnumValues keyEnums,
TypeSerializer vts, BeanProperty property, JsonSerializer<Object> valueSerializer)
{
super(EnumMap.class, false);
_staticTyping = staticTyping || (valueType != null && valueType.isFinal());
_valueType = valueType;
_keyEnums = keyEnums;
_valueTypeSerializer = vts;
_property = property;
_valueSerializer = valueSerializer;
}
@Override
public ContainerSerializerBase<?> _withValueTypeSerializer(TypeSerializer vts)
{
return new EnumMapSerializer(_valueType, _staticTyping, _keyEnums, vts, _property, _valueSerializer);
}
@Override
public void serialize(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeStartObject();
if (!value.isEmpty()) {
serializeContents(value, jgen, provider);
}
jgen.writeEndObject();
}
@Override
public void serializeWithType(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
{
typeSer.writeTypePrefixForObject(value, jgen);
if (!value.isEmpty()) {
serializeContents(value, jgen, provider);
}
typeSer.writeTypeSuffixForObject(value, jgen);
}
protected void serializeContents(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
if (_valueSerializer != null) {
serializeContentsUsing(value, jgen, provider, _valueSerializer);
return;
}
JsonSerializer<Object> prevSerializer = null;
Class<?> prevClass = null;
EnumValues keyEnums = _keyEnums;
for (Map.Entry<? extends Enum<?>,?> entry : value.entrySet()) {
// First, serialize key
Enum<?> key = entry.getKey();
if (keyEnums == null) {
/* 15-Oct-2009, tatu: This is clumsy, but still the simplest efficient
* way to do it currently, as Serializers get cached. (it does assume we'll always use
* default serializer tho -- so ideally code should be rewritten)
*/
// ... and lovely two-step casting process too...
SerializerBase<?> ser = (SerializerBase<?>) provider.findValueSerializer(
key.getDeclaringClass(), _property);
keyEnums = ((EnumSerializer) ser).getEnumValues();
}
jgen.writeFieldName(keyEnums.serializedValueFor(key));
// And then value
Object valueElem = entry.getValue();
if (valueElem == null) {
provider.defaultSerializeNull(jgen);
} else {
Class<?> cc = valueElem.getClass();
JsonSerializer<Object> currSerializer;
if (cc == prevClass) {
currSerializer = prevSerializer;
} else {
currSerializer = provider.findValueSerializer(cc, _property);
prevSerializer = currSerializer;
prevClass = cc;
}
try {
currSerializer.serialize(valueElem, jgen, provider);
} catch (Exception e) {
// [JACKSON-55] Need to add reference information
wrapAndThrow(provider, e, value, entry.getKey().name());
}
}
}
}
protected void serializeContentsUsing(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider,
JsonSerializer<Object> valueSer)
throws IOException, JsonGenerationException
{
EnumValues keyEnums = _keyEnums;
for (Map.Entry<? extends Enum<?>,?> entry : value.entrySet()) {
Enum<?> key = entry.getKey();
if (keyEnums == null) {
// clumsy, but has to do for now:
SerializerBase<?> ser = (SerializerBase<?>) provider.findValueSerializer(key.getDeclaringClass(),
_property);
keyEnums = ((EnumSerializer) ser).getEnumValues();
}
jgen.writeFieldName(keyEnums.serializedValueFor(key));
Object valueElem = entry.getValue();
if (valueElem == null) {
provider.defaultSerializeNull(jgen);
} else {
try {
valueSer.serialize(valueElem, jgen, provider);
} catch (Exception e) {
wrapAndThrow(provider, e, value, entry.getKey().name());
}
}
}
}
@Override
public void resolve(SerializerProvider provider)
throws JsonMappingException
{
if (_staticTyping && _valueSerializer == null) {
_valueSerializer = provider.findValueSerializer(_valueType, _property);
}
}
@SuppressWarnings("unchecked")
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
ObjectNode o = createSchemaNode("object", true);
if (typeHint instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments();
if (typeArgs.length == 2) {
JavaType enumType = provider.constructType(typeArgs[0]);
JavaType valueType = provider.constructType(typeArgs[1]);
ObjectNode propsNode = JsonNodeFactory.instance.objectNode();
Class<Enum<?>> enumClass = (Class<Enum<?>>) enumType.getRawClass();
for (Enum<?> enumValue : enumClass.getEnumConstants()) {
JsonSerializer<Object> ser = provider.findValueSerializer(valueType.getRawClass(), _property);
JsonNode schemaNode = (ser instanceof SchemaAware) ?
((SchemaAware) ser).getSchema(provider, null) :
JsonSchema.getDefaultSchemaNode();
propsNode.put(provider.getConfig().getAnnotationIntrospector().findEnumValue((Enum<?>)enumValue), schemaNode);
}
o.put("properties", propsNode);
}
}
return o;
}
}