Skip to content

Commit 9566ea8

Browse files
committed
Fix #943
1 parent d16a507 commit 9566ea8

File tree

7 files changed

+116
-29
lines changed

7 files changed

+116
-29
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Project: jackson-databind
1616
(contributed by Sergio M)
1717
#941: Deserialization from "{}" to ObjectNode field causes "out of END_OBJECT token" error
1818
(reported by Sadayuki F)
19+
#943: Incorrect serialization of enum map key
20+
(reported by Benson M)
1921
#944: Failure to use custom deserializer for key deserializer
2022
(contributed by Benson M)
2123
#949: Report the offending substring when number parsing fails

src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -751,15 +751,23 @@ public TypeSerializer findTypeSerializer(JavaType javaType) throws JsonMappingEx
751751
* Note that the serializer itself can be called with instances
752752
* of any Java object, but not nulls.
753753
*/
754-
public JsonSerializer<Object> findKeySerializer(JavaType keyType,
755-
BeanProperty property)
754+
public JsonSerializer<Object> findKeySerializer(JavaType keyType, BeanProperty property)
756755
throws JsonMappingException
757756
{
758757
JsonSerializer<Object> ser = _serializerFactory.createKeySerializer(_config, keyType, _keySerializer);
759758
// 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too
760759
return _handleContextualResolvable(ser, property);
761760
}
762761

762+
/**
763+
* @since 2.7
764+
*/
765+
public JsonSerializer<Object> findKeySerializer(Class<?> rawKeyType, BeanProperty property)
766+
throws JsonMappingException
767+
{
768+
return findKeySerializer(_config.constructType(rawKeyType), property);
769+
}
770+
763771
/*
764772
/********************************************************
765773
/* Accessors for specialized serializers

src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -838,13 +838,13 @@ public void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider
838838
}
839839
_depositSchemaProperty(propertiesNode, schemaNode);
840840
}
841-
841+
842842
/*
843843
/**********************************************************
844844
/* Helper methods
845845
/**********************************************************
846846
*/
847-
847+
848848
protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
849849
Class<?> type, SerializerProvider provider) throws JsonMappingException
850850
{
@@ -861,7 +861,7 @@ protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
861861
}
862862
return result.serializer;
863863
}
864-
864+
865865
/**
866866
* Method that can be used to access value of the property this
867867
* Object describes, from given bean instance.

src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ public abstract TypeSerializer createTypeSerializer(SerializationConfig config,
7272
* be output as <code>JsonToken.FIELD_NAME</code>) for Map that has specified declared
7373
* key type, and is for specified property (or, if property is null, as root value)
7474
*
75-
* @param baseType Declared type for Map keys
75+
* @param type Declared type for Map keys
7676
* @param defaultImpl Default key serializer implementation to use, if no custom ones
7777
* are found (may be null)
7878
*
7979
* @return Serializer to use, if factory knows it; null if not (in which case default
8080
* serializer is to be used)
8181
*/
8282
public abstract JsonSerializer<Object> createKeySerializer(SerializationConfig config,
83-
JavaType baseType, JsonSerializer<Object> defaultImpl)
83+
JavaType type, JsonSerializer<Object> defaultImpl)
8484
throws JsonMappingException;
8585
}

src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,22 @@ public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type,
129129
return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
130130
}
131131

132+
/**
133+
* Method called if initial lookup fails, when looking for a key
134+
* serializer (possible attached indirectly to a property)
135+
* Will both find serializer
136+
* and construct new map instance if warranted, and return both.
137+
*
138+
* @since 2.7
139+
*/
140+
public final SerializerAndMapResult findAndAddKeySerializer(Class<?> type,
141+
SerializerProvider provider, BeanProperty property)
142+
throws JsonMappingException
143+
{
144+
JsonSerializer<Object> serializer = provider.findKeySerializer(type, property);
145+
return new SerializerAndMapResult(serializer, newWith(type, serializer));
146+
}
147+
132148
/**
133149
* Method that can be used to 'register' a serializer that caller has resolved
134150
* without help of this map.

src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import com.fasterxml.jackson.core.*;
88
import com.fasterxml.jackson.databind.*;
9+
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
910

1011
@SuppressWarnings("serial")
1112
public class StdKeySerializers
@@ -30,28 +31,31 @@ public static JsonSerializer<Object> getStdKeySerializer(SerializationConfig con
3031
// can not be used, since caller has not yet checked for that annotation
3132
// This is why Enum types are not handled here quite yet
3233

33-
if (rawKeyType != null) {
34-
if (rawKeyType == String.class) {
35-
return DEFAULT_STRING_SERIALIZER;
36-
}
37-
if (rawKeyType == Object.class || rawKeyType.isPrimitive()
38-
|| Number.class.isAssignableFrom(rawKeyType)) {
39-
return DEFAULT_KEY_SERIALIZER;
40-
}
41-
if (rawKeyType == Class.class) {
42-
return new Default(Default.TYPE_CLASS, rawKeyType);
43-
}
44-
if (Date.class.isAssignableFrom(rawKeyType)) {
45-
return new Default(Default.TYPE_DATE, rawKeyType);
46-
}
47-
if (Calendar.class.isAssignableFrom(rawKeyType)) {
48-
return new Default(Default.TYPE_CALENDAR, rawKeyType);
49-
}
50-
// other JDK types we know convert properly with 'toString()'?
51-
if (rawKeyType == java.util.UUID.class) {
52-
return new Default(Default.TYPE_TO_STRING, rawKeyType);
53-
}
34+
// [databind#943: Use a dynamic key serializer if we are not given actual
35+
// type declaration
36+
if ((rawKeyType == null) || (rawKeyType == Object.class)) {
37+
// !!! TODO
38+
return new Dynamic();
39+
}
5440

41+
if (rawKeyType == String.class) {
42+
return DEFAULT_STRING_SERIALIZER;
43+
}
44+
if (rawKeyType.isPrimitive() || Number.class.isAssignableFrom(rawKeyType)) {
45+
return DEFAULT_KEY_SERIALIZER;
46+
}
47+
if (rawKeyType == Class.class) {
48+
return new Default(Default.TYPE_CLASS, rawKeyType);
49+
}
50+
if (Date.class.isAssignableFrom(rawKeyType)) {
51+
return new Default(Default.TYPE_DATE, rawKeyType);
52+
}
53+
if (Calendar.class.isAssignableFrom(rawKeyType)) {
54+
return new Default(Default.TYPE_CALENDAR, rawKeyType);
55+
}
56+
// other JDK types we know convert properly with 'toString()'?
57+
if (rawKeyType == java.util.UUID.class) {
58+
return new Default(Default.TYPE_TO_STRING, rawKeyType);
5559
}
5660
return useDefault ? DEFAULT_KEY_SERIALIZER : null;
5761
}
@@ -94,6 +98,14 @@ public static JsonSerializer<Object> getDefault() {
9498
/**********************************************************
9599
*/
96100

101+
/**
102+
* This is a "chameleon" style multi-type key serializer for simple
103+
* standard JDK types.
104+
*<p>
105+
* TODO: Should (but does not yet) support re-configuring format used for
106+
* {@link java.util.Date} and {@link java.util.Calendar} key serializers,
107+
* as well as alternative configuration of Enum key serializers.
108+
*/
97109
public static class Default extends StdSerializer<Object> {
98110
final static int TYPE_DATE = 1;
99111
final static int TYPE_CALENDAR = 2;
@@ -134,6 +146,55 @@ public void serialize(Object value, JsonGenerator g, SerializerProvider provider
134146
}
135147
}
136148

149+
/**
150+
* Key serializer used when key type is not known statically, and actual key
151+
* serializer needs to be dynamically located.
152+
*/
153+
public static class Dynamic extends StdSerializer<Object>
154+
{
155+
// Important: MUST be transient, to allow serialization of key serializer itself
156+
protected transient PropertySerializerMap _dynamicSerializers;
157+
158+
public Dynamic() {
159+
super(String.class, false);
160+
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
161+
}
162+
163+
Object readResolve() {
164+
// Since it's transient, and since JDK serialization by-passes ctor, need this:
165+
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
166+
return this;
167+
}
168+
169+
@Override
170+
public void serialize(Object value, JsonGenerator g, SerializerProvider provider)
171+
throws IOException {
172+
Class<?> cls = value.getClass();
173+
PropertySerializerMap m = _dynamicSerializers;
174+
JsonSerializer<Object> ser = m.serializerFor(cls);
175+
if (ser == null) {
176+
ser = _findAndAddDynamic(m, cls, provider);
177+
}
178+
ser.serialize(value, g, provider);
179+
}
180+
181+
protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
182+
Class<?> type, SerializerProvider provider) throws JsonMappingException
183+
{
184+
PropertySerializerMap.SerializerAndMapResult result =
185+
// null -> for now we won't keep ref or pass BeanProperty; could change
186+
map.findAndAddKeySerializer(type, provider, null);
187+
// did we get a new map of serializers? If so, start using it
188+
if (map != result.map) {
189+
_dynamicSerializers = result.map;
190+
}
191+
return result.serializer;
192+
}
193+
}
194+
195+
/**
196+
* Simple and fast key serializer when keys are Strings.
197+
*/
137198
public static class StringKeySerializer extends StdSerializer<Object>
138199
{
139200
public StringKeySerializer() { super(String.class, false); }

src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,6 @@ public void testDynamicMapKeys() throws Exception
188188
stuff.put(AbcLC.B, Integer.valueOf(3));
189189
stuff.put(new UCString("foo"), Integer.valueOf(4));
190190
String json = MAPPER.writeValueAsString(stuff);
191-
assertEquals("{'b':3,'FOO':4}", aposToQuotes(json));
191+
assertEquals(aposToQuotes("{'b':3,'FOO':4}"), json);
192192
}
193193
}

0 commit comments

Comments
 (0)