package com.fasterxml.jackson.databind.ser.std;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.EnumValues;

@SuppressWarnings("serial")
public abstract class StdKeySerializers
{
    @SuppressWarnings("deprecation")
    protected final static JsonSerializer<Object> DEFAULT_KEY_SERIALIZER = new StdKeySerializer();

    protected final static JsonSerializer<Object> DEFAULT_STRING_SERIALIZER = new StringKeySerializer();

    
Params:
  • config – Serialization configuration in use, may be needed in choosing serializer to use
  • rawKeyType – Type of key values to serialize
  • useDefault – If no match is found, should we return fallback deserializer (true), or null (false)?
/** * @param config Serialization configuration in use, may be needed in choosing * serializer to use * @param rawKeyType Type of key values to serialize * @param useDefault If no match is found, should we return fallback deserializer * (true), or null (false)? */
public static JsonSerializer<Object> getStdKeySerializer(SerializationConfig config, Class<?> rawKeyType, boolean useDefault) { // 24-Sep-2015, tatu: Important -- should ONLY consider types for which `@JsonValue` // cannot be used, since caller has not yet checked for that annotation // This is why Enum types are not handled here quite yet // [databind#943: Use a dynamic key serializer if we are not given actual // type declaration if ((rawKeyType == null) || (rawKeyType == Object.class)) { return new Dynamic(); } if (rawKeyType == String.class) { return DEFAULT_STRING_SERIALIZER; } if (rawKeyType.isPrimitive()) { rawKeyType = ClassUtil.wrapperType(rawKeyType); } if (rawKeyType == Integer.class) { return new Default(Default.TYPE_INTEGER, rawKeyType); } if (rawKeyType == Long.class) { return new Default(Default.TYPE_LONG, rawKeyType); } if (rawKeyType.isPrimitive() || Number.class.isAssignableFrom(rawKeyType)) { // 28-Jun-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER, but makes // more sense to use simpler one directly return new Default(Default.TYPE_TO_STRING, rawKeyType); } if (rawKeyType == Class.class) { return new Default(Default.TYPE_CLASS, rawKeyType); } if (Date.class.isAssignableFrom(rawKeyType)) { return new Default(Default.TYPE_DATE, rawKeyType); } if (Calendar.class.isAssignableFrom(rawKeyType)) { return new Default(Default.TYPE_CALENDAR, rawKeyType); } // other JDK types we know convert properly with 'toString()'? if (rawKeyType == java.util.UUID.class) { return new Default(Default.TYPE_TO_STRING, rawKeyType); } if (rawKeyType == byte[].class) { return new Default(Default.TYPE_BYTE_ARRAY, rawKeyType); } if (useDefault) { // 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not: return new Default(Default.TYPE_TO_STRING, rawKeyType); } return null; }
Method called if no specified key serializer was located; will return a "default" key serializer.
Since:2.7
/** * Method called if no specified key serializer was located; will return a * "default" key serializer. * * @since 2.7 */
@SuppressWarnings("unchecked") public static JsonSerializer<Object> getFallbackKeySerializer(SerializationConfig config, Class<?> rawKeyType) { if (rawKeyType != null) { // 29-Sep-2015, tatu: Odd case here, of `Enum`, which we may get for `EnumMap`; not sure // if that is a bug or feature. Regardless, it seems to require dynamic handling // (compared to getting actual fully typed Enum). // Note that this might even work from the earlier point, but let's play it safe for now // 11-Aug-2016, tatu: Turns out we get this if `EnumMap` is the root value because // then there is no static type if (rawKeyType == Enum.class) { return new Dynamic(); } if (rawKeyType.isEnum()) { return EnumKeySerializer.construct(rawKeyType, EnumValues.constructFromName(config, (Class<Enum<?>>) rawKeyType)); } } // 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not: return new Default(Default.TYPE_TO_STRING, rawKeyType); }
Deprecated:since 2.7
/** * @deprecated since 2.7 */
@Deprecated public static JsonSerializer<Object> getDefault() { return DEFAULT_KEY_SERIALIZER; } /* /********************************************************** /* Standard implementations used /********************************************************** */
This is a "chameleon" style multi-type key serializer for simple standard JDK types.

TODO: Should (but does not yet) support re-configuring format used for Date and Calendar key serializers, as well as alternative configuration of Enum key serializers.

/** * This is a "chameleon" style multi-type key serializer for simple * standard JDK types. *<p> * TODO: Should (but does not yet) support re-configuring format used for * {@link java.util.Date} and {@link java.util.Calendar} key serializers, * as well as alternative configuration of Enum key serializers. */
public static class Default extends StdSerializer<Object> { final static int TYPE_DATE = 1; final static int TYPE_CALENDAR = 2; final static int TYPE_CLASS = 3; final static int TYPE_ENUM = 4; final static int TYPE_INTEGER = 5; // since 2.9 final static int TYPE_LONG = 6; // since 2.9 final static int TYPE_BYTE_ARRAY = 7; // since 2.9 final static int TYPE_TO_STRING = 8; protected final int _typeId; public Default(int typeId, Class<?> type) { super(type, false); _typeId = typeId; } @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { switch (_typeId) { case TYPE_DATE: provider.defaultSerializeDateKey((Date)value, g); break; case TYPE_CALENDAR: provider.defaultSerializeDateKey(((Calendar) value).getTimeInMillis(), g); break; case TYPE_CLASS: g.writeFieldName(((Class<?>)value).getName()); break; case TYPE_ENUM: { String key; if (provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { key = value.toString(); } else { Enum<?> e = (Enum<?>) value; if (provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)) { key = String.valueOf(e.ordinal()); } else { key = e.name(); } } g.writeFieldName(key); } break; case TYPE_INTEGER: case TYPE_LONG: g.writeFieldId(((Number) value).longValue()); break; case TYPE_BYTE_ARRAY: { String encoded = provider.getConfig().getBase64Variant().encode((byte[]) value); g.writeFieldName(encoded); } break; case TYPE_TO_STRING: default: g.writeFieldName(value.toString()); } } }
Key serializer used when key type is not known statically, and actual key serializer needs to be dynamically located.
/** * Key serializer used when key type is not known statically, and actual key * serializer needs to be dynamically located. */
public static class Dynamic extends StdSerializer<Object> { // Important: MUST be transient, to allow serialization of key serializer itself protected transient PropertySerializerMap _dynamicSerializers; public Dynamic() { super(String.class, false); _dynamicSerializers = PropertySerializerMap.emptyForProperties(); } Object readResolve() { // Since it's transient, and since JDK serialization by-passes ctor, need this: _dynamicSerializers = PropertySerializerMap.emptyForProperties(); return this; } @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { Class<?> cls = value.getClass(); PropertySerializerMap m = _dynamicSerializers; JsonSerializer<Object> ser = m.serializerFor(cls); if (ser == null) { ser = _findAndAddDynamic(m, cls, provider); } ser.serialize(value, g, provider); } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { visitStringFormat(visitor, typeHint); } protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map, Class<?> type, SerializerProvider provider) throws JsonMappingException { // 27-Jun-2017, tatu: [databind#1679] Need to avoid StackOverflowError... if (type == Object.class) { // basically just need to call `toString()`, easiest way: JsonSerializer<Object> ser = new Default(Default.TYPE_TO_STRING, type); _dynamicSerializers = map.newWith(type, ser); return ser; } PropertySerializerMap.SerializerAndMapResult result = // null -> for now we won't keep ref or pass BeanProperty; could change map.findAndAddKeySerializer(type, provider, null); // did we get a new map of serializers? If so, start using it if (map != result.map) { _dynamicSerializers = result.map; } return result.serializer; } }
Simple and fast key serializer when keys are Strings.
/** * Simple and fast key serializer when keys are Strings. */
public static class StringKeySerializer extends StdSerializer<Object> { public StringKeySerializer() { super(String.class, false); } @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { g.writeFieldName((String) value); } }
Specialized instance to use for Enum keys, as per [databind#1322]
Since:2.8
/** * Specialized instance to use for Enum keys, as per [databind#1322] * * @since 2.8 */
public static class EnumKeySerializer extends StdSerializer<Object> { protected final EnumValues _values; protected EnumKeySerializer(Class<?> enumType, EnumValues values) { super(enumType, false); _values = values; } public static EnumKeySerializer construct(Class<?> enumType, EnumValues enumValues) { return new EnumKeySerializer(enumType, enumValues); } @Override public void serialize(Object value, JsonGenerator g, SerializerProvider serializers) throws IOException { if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { g.writeFieldName(value.toString()); return; } Enum<?> en = (Enum<?>) value; if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)) { g.writeFieldName(String.valueOf(en.ordinal())); return; } g.writeFieldName(_values.serializedValueFor(en)); } } }