Skip to content

Commit 3e89294

Browse files
committed
Fixed #1552
1 parent 9dc7389 commit 3e89294

File tree

8 files changed

+88
-23
lines changed

8 files changed

+88
-23
lines changed

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Project: jackson-databind
6262
(reported by Lyor G)
6363
#1550: Unexpected behavior with `@JsonInclude(JsonInclude.Include.NON_EMPTY)` and
6464
`java.util.Date` serialization
65+
#1552: Map key converted to byte array is not serialized as base64 string
66+
(reported by nmatt@github)
6567
#1554: Support deserialization of `Shape.OBJECT` ("as POJO") for `Map`s (and map-like types)
6668

6769
2.8.8 (not yet released)

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

+4-16
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,7 @@
44
import java.lang.reflect.Type;
55
import java.net.URL;
66
import java.text.DateFormat;
7-
import java.util.ArrayList;
8-
import java.util.Collection;
9-
import java.util.LinkedHashSet;
10-
import java.util.List;
11-
import java.util.Locale;
12-
import java.util.Map;
13-
import java.util.ServiceLoader;
14-
import java.util.Set;
15-
import java.util.TimeZone;
7+
import java.util.*;
168
import java.util.concurrent.ConcurrentHashMap;
179
import java.util.concurrent.atomic.AtomicReference;
1810

@@ -23,12 +15,7 @@
2315
import com.fasterxml.jackson.core.type.ResolvedType;
2416
import com.fasterxml.jackson.core.type.TypeReference;
2517
import com.fasterxml.jackson.core.util.*;
26-
import com.fasterxml.jackson.databind.cfg.BaseSettings;
27-
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
28-
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
29-
import com.fasterxml.jackson.databind.cfg.MapperConfig;
30-
import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
31-
import com.fasterxml.jackson.databind.cfg.ConfigOverrides;
18+
import com.fasterxml.jackson.databind.cfg.*;
3219
import com.fasterxml.jackson.databind.deser.*;
3320
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
3421
import com.fasterxml.jackson.databind.introspect.*;
@@ -1769,7 +1756,8 @@ public ObjectMapper setConfig(SerializationConfig config) {
17691756
* {@link JsonFactory}, so changes to its configuration will "leak".
17701757
* To avoid such observed changes you should always use "with()" and
17711758
* "without()" method of {@link ObjectReader} and {@link ObjectWriter}
1772-
* for changing {@link JsonParser#Feature} and {@link JsonGenerator#Feature}
1759+
* for changing {@link com.fasterxml.jackson.core.JsonParser.Feature}
1760+
* and {@link com.fasterxml.jackson.core.JsonGenerator.Feature}
17731761
* settings to use on per-call basis.
17741762
*
17751763
* @return {@link JsonFactory} that this mapper uses when it needs to

src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java

+17-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class StdKeyDeserializer extends KeyDeserializer
4747
public final static int TYPE_URL = 14;
4848
public final static int TYPE_CLASS = 15;
4949
public final static int TYPE_CURRENCY = 16;
50+
public final static int TYPE_BYTE_ARRAY = 17;
5051

5152
final protected int _kind;
5253
final protected Class<?> _keyClass;
@@ -108,6 +109,8 @@ public static StdKeyDeserializer forType(Class<?> raw)
108109
} else if (raw == Currency.class) {
109110
FromStringDeserializer<?> deser = FromStringDeserializer.findDeserializer(Currency.class);
110111
return new StdKeyDeserializer(TYPE_CURRENCY, raw, deser);
112+
} else if (raw == byte[].class) {
113+
kind = TYPE_BYTE_ARRAY;
111114
} else {
112115
return null;
113116
}
@@ -204,26 +207,32 @@ protected Object _parse(String key, DeserializationContext ctxt) throws Exceptio
204207
try {
205208
return UUID.fromString(key);
206209
} catch (Exception e) {
207-
return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
210+
return _weirdKey(ctxt, key, e);
208211
}
209212
case TYPE_URI:
210213
try {
211214
return URI.create(key);
212215
} catch (Exception e) {
213-
return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
216+
return _weirdKey(ctxt, key, e);
214217
}
215218
case TYPE_URL:
216219
try {
217220
return new URL(key);
218221
} catch (MalformedURLException e) {
219-
return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
222+
return _weirdKey(ctxt, key, e);
220223
}
221224
case TYPE_CLASS:
222225
try {
223226
return ctxt.findClass(key);
224227
} catch (Exception e) {
225228
return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as Class");
226229
}
230+
case TYPE_BYTE_ARRAY:
231+
try {
232+
return ctxt.getConfig().getBase64Variant().decode(key);
233+
} catch (Exception e) {
234+
return _weirdKey(ctxt, key, e);
235+
}
227236
default:
228237
throw new IllegalStateException("Internal error: unknown key type "+_keyClass);
229238
}
@@ -247,6 +256,11 @@ protected double _parseDouble(String key) throws IllegalArgumentException {
247256
return NumberInput.parseDouble(key);
248257
}
249258

259+
// @since 2.9
260+
protected Object _weirdKey(DeserializationContext ctxt, String key, Exception e) throws IOException {
261+
return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
262+
}
263+
250264
/*
251265
/**********************************************************
252266
/* First: the standard "String as String" deserializer

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public static JsonSerializer<Object> getStdKeySerializer(SerializationConfig con
6464
if (rawKeyType == java.util.UUID.class) {
6565
return new Default(Default.TYPE_TO_STRING, rawKeyType);
6666
}
67+
if (rawKeyType == byte[].class) {
68+
return new Default(Default.TYPE_BYTE_ARRAY, rawKeyType);
69+
}
6770
if (useDefault) {
6871
// 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not:
6972
return new Default(Default.TYPE_TO_STRING, rawKeyType);
@@ -129,7 +132,8 @@ public static class Default extends StdSerializer<Object> {
129132
final static int TYPE_ENUM = 4;
130133
final static int TYPE_INTEGER = 5; // since 2.9
131134
final static int TYPE_LONG = 6; // since 2.9
132-
final static int TYPE_TO_STRING = 7;
135+
final static int TYPE_BYTE_ARRAY = 7; // since 2.9
136+
final static int TYPE_TO_STRING = 8;
133137

134138
protected final int _typeId;
135139

@@ -161,6 +165,12 @@ public void serialize(Object value, JsonGenerator g, SerializerProvider provider
161165
case TYPE_LONG:
162166
g.writeFieldId(((Number) value).longValue());
163167
break;
168+
case TYPE_BYTE_ARRAY:
169+
{
170+
String encoded = provider.getConfig().getBase64Variant().encode((byte[]) value);
171+
g.writeFieldName(encoded);
172+
}
173+
break;
164174
case TYPE_TO_STRING:
165175
default:
166176
g.writeFieldName(value.toString());

src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,14 @@ protected static class MapWrapper<K,V>
109109
{
110110
public Map<K,V> map;
111111

112+
public MapWrapper() { }
112113
public MapWrapper(Map<K,V> m) {
113114
map = m;
114115
}
116+
public MapWrapper(K key, V value) {
117+
map = new LinkedHashMap<>();
118+
map.put(key, value);
119+
}
115120
}
116121

117122
protected static class ArrayWrapper<T>

src/test/java/com/fasterxml/jackson/databind/deser/MapDeserializationTest.java renamed to src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.databind.deser;
1+
package com.fasterxml.jackson.databind.deser.jdk;
22

33
import java.io.IOException;
44
import java.text.DateFormat;

src/test/java/com/fasterxml/jackson/databind/deser/MapKeyDeserializationTest.java renamed to src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
package com.fasterxml.jackson.databind.deser;
1+
package com.fasterxml.jackson.databind.deser.jdk;
22

33
import java.util.Map;
44

55
import com.fasterxml.jackson.annotation.JsonCreator;
66
import com.fasterxml.jackson.annotation.JsonValue;
7+
import com.fasterxml.jackson.core.Base64Variants;
78
import com.fasterxml.jackson.core.type.TypeReference;
89
import com.fasterxml.jackson.databind.*;
910

11+
import org.junit.Assert;
12+
1013
public class MapKeyDeserializationTest extends BaseMapTest
1114
{
1215
static class FullName {
@@ -34,15 +37,39 @@ public String toString() {
3437
}
3538
}
3639

40+
/*
41+
/**********************************************************
42+
/* Test methods
43+
/**********************************************************
44+
*/
45+
46+
final private ObjectMapper MAPPER = objectMapper();
47+
3748
public void testDeserializeKeyViaFactory() throws Exception
3849
{
3950
Map<FullName, Double> map =
40-
new ObjectMapper().readValue("{\"first.last\": 42}",
51+
MAPPER.readValue("{\"first.last\": 42}",
4152
new TypeReference<Map<FullName, Double>>() { });
4253
Map.Entry<FullName, Double> entry = map.entrySet().iterator().next();
4354
FullName key = entry.getKey();
4455
assertEquals(key._firstname, "first");
4556
assertEquals(key._lastname, "last");
4657
assertEquals(entry.getValue().doubleValue(), 42, 0);
4758
}
59+
60+
public void testByteArrayKeyDeserialization() throws Exception
61+
{
62+
byte[] binary = new byte[] { 1, 2, 4, 8, 16, 33, 79 };
63+
String encoded = Base64Variants.MIME.encode(binary);
64+
65+
// First, using wrapper
66+
MapWrapper<byte[], String> result = MAPPER.readValue(
67+
aposToQuotes("{'map':{'"+encoded+"':'foobar'}}"),
68+
new TypeReference<MapWrapper<byte[], String>>() { });
69+
assertEquals(1, result.map.size());
70+
Map.Entry<byte[],String> entry = result.map.entrySet().iterator().next();
71+
assertEquals("foobar", entry.getValue());
72+
byte[] key = entry.getKey();
73+
Assert.assertArrayEquals(binary, key);
74+
}
4875
}

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

+19
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,23 @@ public void testConcurrentMaps() throws Exception
310310
json = w.writeValueAsString(input);
311311
assertEquals(aposToQuotes("{'a':'b','x':'y'}"), json);
312312
}
313+
314+
// [databind#1552]
315+
public void testMapsWithBinaryKeys() throws Exception
316+
{
317+
byte[] binary = new byte[] { 1, 2, 3, 4, 5 };
318+
319+
// First, using wrapper
320+
MapWrapper<byte[], String> input = new MapWrapper<>(binary, "stuff");
321+
String expBase64 = Base64Variants.MIME.encode(binary);
322+
323+
assertEquals(aposToQuotes("{'map':{'"+expBase64+"':'stuff'}}"),
324+
MAPPER.writeValueAsString(input));
325+
326+
// and then dynamically..
327+
Map<byte[],String> map = new LinkedHashMap<>();
328+
map.put(binary, "xyz");
329+
assertEquals(aposToQuotes("{'"+expBase64+"':'xyz'}"),
330+
MAPPER.writeValueAsString(map));
331+
}
313332
}

0 commit comments

Comments
 (0)