Skip to content

Commit f07bda2

Browse files
committed
Fixed #2815 (add Shape overrides for UUIDSerializer)
1 parent 472c071 commit f07bda2

File tree

4 files changed

+124
-47
lines changed

4 files changed

+124
-47
lines changed

release-notes/VERSION-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Project: jackson-databind
1414
(reported by isaki@github)
1515
#2796: `TypeFactory.constructType()` does not take `TypeBindings` correctly
1616
(reported by Daniel H)
17+
#2815: Add `JsonFormat.Shape` awareness for UUID serialization (`UUIDSerializer`)
1718

1819
2.11.1 (25-Jun-2020)
1920

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

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,24 @@ public class UUIDSerializer
2525
{
2626
final static char[] HEX_CHARS = "0123456789abcdef".toCharArray();
2727

28-
public UUIDSerializer() { super(UUID.class); }
28+
/**
29+
* Configuration setting that indicates if serialization as binary
30+
* (native or Base64-encoded) has been forced; {@code null} means
31+
* "use default heuristic"
32+
*
33+
* @since 2.11.3
34+
*/
35+
protected final Boolean _asBinary;
36+
37+
public UUIDSerializer() { this(null); }
38+
39+
/**
40+
* @since 2.11.3
41+
*/
42+
protected UUIDSerializer(Boolean asBinary) {
43+
super(UUID.class);
44+
_asBinary = asBinary;
45+
}
2946

3047
@Override
3148
public boolean isEmpty(SerializerProvider prov, UUID value)
@@ -45,6 +62,7 @@ public JsonSerializer<?> createContextual(SerializerProvider serializers,
4562
JsonFormat.Value format = findFormatOverrides(serializers,
4663
property, handledType());
4764
Boolean asBinary = null;
65+
4866
if (format != null) {
4967
JsonFormat.Shape shape = format.getShape();
5068
if (shape == JsonFormat.Shape.BINARY) {
@@ -54,7 +72,9 @@ public JsonSerializer<?> createContextual(SerializerProvider serializers,
5472
}
5573
// otherwise leave as `null` meaning about same as NATURAL
5674
}
57-
// !!! TODO:
75+
if (asBinary != _asBinary) {
76+
return new UUIDSerializer(asBinary);
77+
}
5878
return this;
5979
}
6080

@@ -63,15 +83,9 @@ public void serialize(UUID value, JsonGenerator gen, SerializerProvider provider
6383
throws IOException
6484
{
6585
// First: perhaps we could serialize it as raw binary data?
66-
if (gen.canWriteBinaryNatively()) {
67-
// 07-Dec-2013, tatu: One nasty case; that of TokenBuffer. While it can
68-
// technically retain binary data, we do not want to do use binary
69-
// with it, as that results in UUIDs getting converted to Base64 for
70-
// most conversions.
71-
if (!(gen instanceof TokenBuffer)) {
72-
gen.writeBinary(_asBytes(value));
73-
return;
74-
}
86+
if (_writeAsBinary(gen)) {
87+
gen.writeBinary(_asBytes(value));
88+
return;
7589
}
7690

7791
// UUID.toString() works ok functionally, but we can make it go much faster
@@ -96,6 +110,20 @@ public void serialize(UUID value, JsonGenerator gen, SerializerProvider provider
96110
gen.writeString(ch, 0, 36);
97111
}
98112

113+
// @since 2.11.3
114+
protected boolean _writeAsBinary(JsonGenerator g)
115+
{
116+
if (_asBinary != null) {
117+
return _asBinary;
118+
}
119+
// 07-Dec-2013, tatu: One nasty case; that of TokenBuffer. While it can
120+
// technically retain binary data, we do not want to do use binary
121+
// with it, as that results in UUIDs getting converted to Base64 for
122+
// most conversions.
123+
return !(g instanceof TokenBuffer) && g.canWriteBinaryNatively();
124+
}
125+
126+
99127
// Need to add bit of extra info, format
100128
@Override
101129
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)

src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
* that are not Enums, Collections, Maps, or standard Date/Time types)
1919
*/
2020
public class JDKTypeSerializationTest
21-
extends com.fasterxml.jackson.databind.BaseMapTest
21+
extends BaseMapTest
2222
{
23-
private final ObjectMapper MAPPER = objectMapper();
23+
private final ObjectMapper MAPPER = sharedMapper();
2424

2525
static class InetAddressBean {
2626
public InetAddress value;
@@ -182,40 +182,6 @@ public void testDuplicatedByteBufferWithCustomPosition() throws IOException
182182
assertEquals(exp, MAPPER.writeValueAsString(bbuf.duplicate()));
183183
}
184184

185-
// Verify that efficient UUID codec won't mess things up:
186-
public void testUUIDs() throws IOException
187-
{
188-
// first, couple of generated UUIDs:
189-
for (String value : new String[] {
190-
"76e6d183-5f68-4afa-b94a-922c1fdb83f8",
191-
"540a88d1-e2d8-4fb1-9396-9212280d0a7f",
192-
"2c9e441d-1cd0-472d-9bab-69838f877574",
193-
"591b2869-146e-41d7-8048-e8131f1fdec5",
194-
"82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
195-
"00000007-0000-0000-0000-000000000000"
196-
}) {
197-
UUID uuid = UUID.fromString(value);
198-
String json = MAPPER.writeValueAsString(uuid);
199-
assertEquals(quote(uuid.toString()), json);
200-
201-
// Also, wrt [#362], should convert cleanly
202-
String str = MAPPER.convertValue(uuid, String.class);
203-
assertEquals(value, str);
204-
}
205-
206-
// then use templating; note that these are not exactly valid UUIDs
207-
// wrt spec (type bits etc), but JDK UUID should deal ok
208-
final String TEMPL = "00000000-0000-0000-0000-000000000000";
209-
final String chars = "123456789abcdef";
210-
211-
for (int i = 0; i < chars.length(); ++i) {
212-
String value = TEMPL.replace('0', chars.charAt(i));
213-
UUID uuid = UUID.fromString(value);
214-
String json = MAPPER.writeValueAsString(uuid);
215-
assertEquals(quote(uuid.toString()), json);
216-
}
217-
}
218-
219185
// [databind#2197]
220186
public void testVoidSerialization() throws Exception
221187
{
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.fasterxml.jackson.databind.ser.jdk;
2+
3+
import java.io.IOException;
4+
import java.util.UUID;
5+
6+
import com.fasterxml.jackson.annotation.JsonFormat;
7+
8+
import com.fasterxml.jackson.databind.*;
9+
10+
public class UUIDSerializationTest extends BaseMapTest
11+
{
12+
static class UUIDWrapperVanilla {
13+
public UUID uuid;
14+
15+
public UUIDWrapperVanilla(UUID u) { uuid = u; }
16+
}
17+
18+
static class UUIDWrapperBinary {
19+
// default with JSON is String, for use of (base64-encoded) Binary:
20+
@JsonFormat(shape = JsonFormat.Shape.BINARY)
21+
public UUID uuid;
22+
23+
public UUIDWrapperBinary(UUID u) { uuid = u; }
24+
}
25+
26+
private final ObjectMapper MAPPER = sharedMapper();
27+
28+
// Verify that efficient UUID codec won't mess things up:
29+
public void testBasicUUIDs() throws IOException
30+
{
31+
// first, couple of generated UUIDs:
32+
for (String value : new String[] {
33+
"76e6d183-5f68-4afa-b94a-922c1fdb83f8",
34+
"540a88d1-e2d8-4fb1-9396-9212280d0a7f",
35+
"2c9e441d-1cd0-472d-9bab-69838f877574",
36+
"591b2869-146e-41d7-8048-e8131f1fdec5",
37+
"82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
38+
"00000007-0000-0000-0000-000000000000"
39+
}) {
40+
UUID uuid = UUID.fromString(value);
41+
String json = MAPPER.writeValueAsString(uuid);
42+
assertEquals(quote(uuid.toString()), json);
43+
44+
// Also, wrt [#362], should convert cleanly
45+
String str = MAPPER.convertValue(uuid, String.class);
46+
assertEquals(value, str);
47+
}
48+
49+
// then use templating; note that these are not exactly valid UUIDs
50+
// wrt spec (type bits etc), but JDK UUID should deal ok
51+
final String TEMPL = "00000000-0000-0000-0000-000000000000";
52+
final String chars = "123456789abcdef";
53+
54+
for (int i = 0; i < chars.length(); ++i) {
55+
String value = TEMPL.replace('0', chars.charAt(i));
56+
UUID uuid = UUID.fromString(value);
57+
String json = MAPPER.writeValueAsString(uuid);
58+
assertEquals(quote(uuid.toString()), json);
59+
}
60+
}
61+
62+
public void testShapeOverrides() throws Exception
63+
{
64+
final String nullUUIDStr = "00000000-0000-0000-0000-000000000000";
65+
final UUID nullUUID = UUID.fromString(nullUUIDStr);
66+
67+
// First, see that Binary per-property override works:
68+
assertEquals("{\"uuid\":\"AAAAAAAAAAAAAAAAAAAAAA==\"}",
69+
MAPPER.writeValueAsString(new UUIDWrapperBinary(nullUUID)));
70+
71+
// but that without one we'd get String
72+
assertEquals("{\"uuid\":\""+nullUUIDStr+"\"}",
73+
MAPPER.writeValueAsString(new UUIDWrapperVanilla(nullUUID)));
74+
75+
// but can also override by type
76+
ObjectMapper m = newJsonMapper();
77+
m.configOverride(UUID.class)
78+
.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.BINARY));
79+
assertEquals("{\"uuid\":\"AAAAAAAAAAAAAAAAAAAAAA==\"}",
80+
m.writeValueAsString(new UUIDWrapperVanilla(nullUUID)));
81+
}
82+
}

0 commit comments

Comments
 (0)