Skip to content

Commit 722edd4

Browse files
authored
Add DeserializationFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION to determine what happens on JsonNode coercion to BigDecimal with NaN (#4195)
1 parent 5612aba commit 722edd4

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

src/main/java/com/fasterxml/jackson/databind/cfg/JsonNodeFeature.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,25 @@ public enum JsonNodeFeature implements DatatypeFeature
6161
*
6262
* @since 2.15
6363
*/
64-
STRIP_TRAILING_BIGDECIMAL_ZEROES(true)
64+
STRIP_TRAILING_BIGDECIMAL_ZEROES(true),
65+
66+
/**
67+
* Determines the behavior when coercing `NaN` to {@link java.math.BigDecimal} with
68+
* {@link com.fasterxml.jackson.databind.DeserializationFeature#USE_BIG_DECIMAL_FOR_FLOATS} enabled.
69+
*
70+
* 1. If set to {@code true}, will throw an {@link com.fasterxml.jackson.databind.exc.InvalidFormatException} for
71+
* attempting to coerce {@code NaN} into {@link java.math.BigDecimal}.
72+
* 2. If set to {@code false}, will simply let coercing {@code NaN} into {@link java.math.BigDecimal} happen,
73+
* regardless of how such coercion will behave --as of 2.16, will simply stay as {@code NaN} of original
74+
* floating-point type node.
75+
*
76+
* <p>
77+
* Default value is {@code false} for backwards-compatibility, but will most likely be changed to
78+
* {@code true} in 3.0.
79+
*
80+
* @since 2.16
81+
*/
82+
FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION(false)
6583
;
6684

6785
private final static int FEATURE_INDEX = DatatypeFeatures.FEATURE_INDEX_JSON_NODE;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,12 @@ protected final JsonNode _fromFloat(JsonParser p, DeserializationContext ctxt,
755755
return _fromBigDecimal(ctxt, nodeFactory, p.getDecimalValue());
756756
}
757757
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
758+
// [databind#4194] Add an option to fail coercing NaN to BigDecimal
759+
// Currently, Jackson 2.x allows such coercion, but Jackson 3.x will not
760+
if (p.isNaN() && ctxt.isEnabled(JsonNodeFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION)) {
761+
ctxt.handleWeirdNumberValue(handledType(), p.getDoubleValue(),
762+
"Cannot convert NaN into BigDecimal");
763+
}
758764
try {
759765
return _fromBigDecimal(ctxt, nodeFactory, p.getDecimalValue());
760766
} catch (NumberFormatException nfe) {

src/test/java/com/fasterxml/jackson/databind/node/NumberNodes1770Test.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.fasterxml.jackson.databind.node;
22

3+
import com.fasterxml.jackson.databind.cfg.JsonNodeFeature;
4+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
35
import java.math.BigDecimal;
46

57
import com.fasterxml.jackson.core.JsonFactory;
@@ -39,4 +41,36 @@ public void testBigDecimalCoercionInf() throws Exception
3941
assertTrue("Expected DoubleNode, got: "+jsonNode.getClass().getName()+": "+jsonNode, jsonNode.isDouble());
4042
assertEquals(Double.POSITIVE_INFINITY, jsonNode.doubleValue());
4143
}
44+
45+
// [databind#4194]: should be able to, by configuration, fail coercing NaN to BigDecimal
46+
public void testBigDecimalCoercionNaN() throws Exception
47+
{
48+
_tryBigDecimalCoercionNaNWithOption(false);
49+
50+
try {
51+
_tryBigDecimalCoercionNaNWithOption(true);
52+
fail("Should not pass");
53+
} catch (InvalidFormatException e) {
54+
verifyException(e, "Cannot convert NaN");
55+
}
56+
}
57+
58+
private void _tryBigDecimalCoercionNaNWithOption(boolean isEnabled) throws Exception
59+
{
60+
JsonFactory factory = JsonFactory.builder()
61+
.enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
62+
.build();
63+
final ObjectReader reader = new JsonMapper(factory)
64+
.reader()
65+
.with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
66+
67+
final String value = "NaN";
68+
// depending on option
69+
final JsonNode jsonNode = isEnabled
70+
? reader.with(JsonNodeFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION).readTree(value)
71+
: reader.without(JsonNodeFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION).readTree(value);
72+
73+
assertTrue("Expected DoubleNode, got: "+jsonNode.getClass().getName()+": "+jsonNode, jsonNode.isDouble());
74+
assertEquals(Double.NaN, jsonNode.doubleValue());
75+
}
4276
}

0 commit comments

Comments
 (0)