Skip to content

Commit 2c8c3f6

Browse files
committed
Backport #2236 fix, accidentally started from master
1 parent 8a32f54 commit 2c8c3f6

File tree

3 files changed

+61
-27
lines changed

3 files changed

+61
-27
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Project: jackson-databind
3333
(reported by RightHandedMonkey@github)
3434
#2230: `WRITE_BIGDECIMAL_AS_PLAIN` is ignored if `@JsonFormat` is used
3535
(reported by Pavel C)
36+
#2236: Type id not provided on `Double.NaN`, `Infinity` with `@JsonTypeInfo`
37+
(reported by C-B-B@github)
3638
#2241: Add `JsonPropertyNamingStrategy.LOWER_DOT_CASE` for dot-delimited names
3739
(contributed by zenglian@github.com)
3840

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import com.fasterxml.jackson.annotation.JsonFormat;
99

1010
import com.fasterxml.jackson.core.*;
11-
11+
import com.fasterxml.jackson.core.type.WritableTypeId;
1212
import com.fasterxml.jackson.databind.*;
1313
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
1414
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
@@ -231,11 +231,26 @@ public void serialize(Object value, JsonGenerator gen,
231231

232232
// IMPORTANT: copied from `NonTypedScalarSerializerBase`
233233
@Override
234-
public void serializeWithType(Object value, JsonGenerator gen,
234+
public void serializeWithType(Object value, JsonGenerator g,
235235
SerializerProvider provider, TypeSerializer typeSer)
236236
throws IOException {
237-
// no type info, just regular serialization
238-
serialize(value, gen, provider);
237+
// 08-Feb-2018, tatu: Except that as per [databind#2236], NaN values need
238+
// special handling
239+
Double d = (Double) value;
240+
if (notFinite(d)) {
241+
WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
242+
// whether to indicate it's number or string is arbitrary; important it is scalar
243+
typeSer.typeId(value, JsonToken.VALUE_NUMBER_FLOAT));
244+
g.writeNumber(d);
245+
typeSer.writeTypeSuffix(g, typeIdDef);
246+
} else {
247+
g.writeNumber(d);
248+
}
249+
}
250+
251+
public static boolean notFinite(double value) {
252+
// `jackson-core` has helper method in 3 but not yet
253+
return Double.isNaN(value) || Double.isInfinite(value);
239254
}
240255
}
241256
}

src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,29 @@ static class Data {
2525
public long key;
2626
}
2727

28+
// Basic `ObjectWrapper` from base uses delegating ctor, won't work well; should
29+
// figure out why, but until then we'll use separate impl
30+
protected static class ObjectWrapperForPoly {
31+
Object object;
32+
33+
protected ObjectWrapperForPoly() { }
34+
public ObjectWrapperForPoly(final Object o) {
35+
object = o;
36+
}
37+
public Object getObject() { return object; }
38+
}
39+
2840
/*
2941
/**********************************************************
3042
/* Test methods
3143
/**********************************************************
3244
*/
33-
45+
46+
private final ObjectMapper DEFAULT_TYPING_MAPPER = newObjectMapper();
47+
{
48+
DEFAULT_TYPING_MAPPER.enableDefaultTyping();
49+
}
50+
3451
/**
3552
* Unit test to verify that limited number of core types do NOT include
3653
* type information, even if declared as Object. This is only done for types
@@ -39,45 +56,36 @@ static class Data {
3956
*/
4057
public void testNumericScalars() throws Exception
4158
{
42-
ObjectMapper m = new ObjectMapper();
43-
m.enableDefaultTyping();
44-
4559
// no typing for Integer, Double, yes for others
46-
assertEquals("[123]", m.writeValueAsString(new Object[] { Integer.valueOf(123) }));
47-
assertEquals("[[\"java.lang.Long\",37]]", m.writeValueAsString(new Object[] { Long.valueOf(37) }));
48-
assertEquals("[0.25]", m.writeValueAsString(new Object[] { Double.valueOf(0.25) }));
49-
assertEquals("[[\"java.lang.Float\",0.5]]", m.writeValueAsString(new Object[] { Float.valueOf(0.5f) }));
60+
assertEquals("[123]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Integer.valueOf(123) }));
61+
assertEquals("[[\"java.lang.Long\",37]]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Long.valueOf(37) }));
62+
assertEquals("[0.25]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Double.valueOf(0.25) }));
63+
assertEquals("[[\"java.lang.Float\",0.5]]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Float.valueOf(0.5f) }));
5064
}
5165

5266
public void testDateScalars() throws Exception
5367
{
54-
ObjectMapper m = new ObjectMapper();
55-
m.enableDefaultTyping();
56-
5768
long ts = 12345678L;
5869
assertEquals("[[\"java.util.Date\","+ts+"]]",
59-
m.writeValueAsString(new Object[] { new Date(ts) }));
70+
DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { new Date(ts) }));
6071

6172
// Calendar is trickier... hmmh. Need to ensure round-tripping
6273
Calendar c = Calendar.getInstance();
6374
c.setTimeInMillis(ts);
64-
String json = m.writeValueAsString(new Object[] { c });
75+
String json = DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { c });
6576
assertEquals("[[\""+c.getClass().getName()+"\","+ts+"]]", json);
6677
// and let's make sure it also comes back same way:
67-
Object[] result = m.readValue(json, Object[].class);
78+
Object[] result = DEFAULT_TYPING_MAPPER.readValue(json, Object[].class);
6879
assertEquals(1, result.length);
6980
assertTrue(result[0] instanceof Calendar);
7081
assertEquals(ts, ((Calendar) result[0]).getTimeInMillis());
7182
}
7283

7384
public void testMiscScalars() throws Exception
7485
{
75-
ObjectMapper m = new ObjectMapper();
76-
m.enableDefaultTyping();
77-
7886
// no typing for Strings, booleans
79-
assertEquals("[\"abc\"]", m.writeValueAsString(new Object[] { "abc" }));
80-
assertEquals("[true,null,false]", m.writeValueAsString(new Boolean[] { true, null, false }));
87+
assertEquals("[\"abc\"]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { "abc" }));
88+
assertEquals("[true,null,false]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Boolean[] { true, null, false }));
8189
}
8290

8391
/**
@@ -101,11 +109,9 @@ public void testScalarArrays() throws Exception
101109

102110
public void test417() throws Exception
103111
{
104-
ObjectMapper m = new ObjectMapper();
105-
m.enableDefaultTyping();
106112
Jackson417Bean input = new Jackson417Bean();
107-
String json = m.writeValueAsString(input);
108-
Jackson417Bean result = m.readValue(json, Jackson417Bean.class);
113+
String json = DEFAULT_TYPING_MAPPER.writeValueAsString(input);
114+
Jackson417Bean result = DEFAULT_TYPING_MAPPER.readValue(json, Jackson417Bean.class);
109115
assertEquals(input.foo, result.foo);
110116
assertEquals(input.bar, result.bar);
111117
}
@@ -136,4 +142,15 @@ public void testDefaultTypingWithLong() throws Exception
136142
assertNotNull(result);
137143
assertEquals(2, result.size());
138144
}
145+
146+
// [databind#2236]: do need type info for NaN
147+
public void testDefaultTypingWithNaN() throws Exception
148+
{
149+
final ObjectWrapperForPoly INPUT = new ObjectWrapperForPoly(Double.POSITIVE_INFINITY);
150+
final String json = DEFAULT_TYPING_MAPPER.writeValueAsString(INPUT);
151+
final ObjectWrapperForPoly result = DEFAULT_TYPING_MAPPER.readValue(json, ObjectWrapperForPoly.class);
152+
assertEquals(Double.class, result.getObject().getClass());
153+
assertEquals(INPUT.getObject().toString(), result.getObject().toString());
154+
assertTrue(((Double) result.getObject()).isInfinite());
155+
}
139156
}

0 commit comments

Comments
 (0)