Skip to content

Commit b67bd63

Browse files
authored
Fix JsonParser.isNaN() to be stable, only indicate explicit NaN values; not ones due to coercion (#1150)
1 parent 6836061 commit b67bd63

File tree

3 files changed

+47
-22
lines changed

3 files changed

+47
-22
lines changed

src/main/java/com/fasterxml/jackson/core/JsonParser.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
1414
import com.fasterxml.jackson.core.exc.InputCoercionException;
15+
import com.fasterxml.jackson.core.json.JsonReadFeature;
1516
import com.fasterxml.jackson.core.type.TypeReference;
1617
import com.fasterxml.jackson.core.util.JacksonFeatureSet;
1718
import com.fasterxml.jackson.core.util.RequestPayload;
@@ -1397,16 +1398,21 @@ public int currentTokenId() {
13971398
public boolean isExpectedNumberIntToken() { return currentToken() == JsonToken.VALUE_NUMBER_INT; }
13981399

13991400
/**
1400-
* Access for checking whether current token is a numeric value token, but
1401-
* one that is of "not-a-number" (NaN) variety (including both "NaN" AND
1402-
* positive/negative infinity!): not supported by all formats,
1403-
* but often supported for {@link JsonToken#VALUE_NUMBER_FLOAT}.
1404-
* NOTE: roughly equivalent to calling <code>!Double.isFinite()</code>
1405-
* on value you would get from calling {@link #getDoubleValue()}.
1406-
*
1407-
* @return {@code True} if the current token is of type {@link JsonToken#VALUE_NUMBER_FLOAT}
1408-
* but represents a "Not a Number"; {@code false} for other tokens and regular
1409-
* floating-point numbers
1401+
* Access for checking whether current token is a special
1402+
* "not-a-number" (NaN) token (including both "NaN" AND
1403+
* positive/negative infinity!). These values are not supported by all formats:
1404+
* JSON, for example, only supports them if
1405+
* {@link JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS} is enabled.
1406+
*<p>
1407+
* NOTE: in case where numeric value is outside range of requested type --
1408+
* most notably {@link java.lang.Float} or {@link java.lang.Double} -- and
1409+
* decoding results effectively in a NaN value, this method DOES NOT return
1410+
* {@code true}: only explicit incoming markers do.
1411+
* This is because value could still be accessed as a valid {@link BigDecimal}.
1412+
*
1413+
* @return {@code True} if the current token is reported as {@link JsonToken#VALUE_NUMBER_FLOAT}
1414+
* and represents a "Not a Number" value; {@code false} for other tokens and regular
1415+
* floating-point numbers.
14101416
*
14111417
* @throws IOException for low-level read issues, or
14121418
* {@link JsonParseException} for decoding problems

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,21 @@ public abstract class ParserBase extends ParserMinimalBase
217217
*/
218218
protected String _numberString;
219219

220+
/**
221+
* Marker for explicit "Not a Number" (NaN) values that may be read
222+
* by some formats: this includes positive and negative infinity,
223+
* as well as "NaN" result for some arithmetic operations.
224+
*<p>
225+
* In case of JSON, such values can only be handled with non-standard
226+
* processing: for some other formats they can be passed normally.
227+
*<p>
228+
* NOTE: this marker is NOT set in case of value overflow/underflow for
229+
* {@code double} or {@code float} values.
230+
*
231+
* @since 2.17
232+
*/
233+
protected boolean _numberIsNaN;
234+
220235
// And then other information about value itself
221236

222237
/**
@@ -571,6 +586,7 @@ protected final JsonToken resetInt(boolean negative, int intLen)
571586
// May throw StreamConstraintsException:
572587
_streamReadConstraints.validateIntegerLength(intLen);
573588
_numberNegative = negative;
589+
_numberIsNaN = false;
574590
_intLength = intLen;
575591
_fractLength = 0;
576592
_expLength = 0;
@@ -584,6 +600,7 @@ protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen,
584600
// May throw StreamConstraintsException:
585601
_streamReadConstraints.validateFPLength(intLen + fractLen + expLen);
586602
_numberNegative = negative;
603+
_numberIsNaN = false;
587604
_intLength = intLen;
588605
_fractLength = fractLen;
589606
_expLength = expLen;
@@ -597,17 +614,15 @@ protected final JsonToken resetAsNaN(String valueStr, double value)
597614
_textBuffer.resetWithString(valueStr);
598615
_numberDouble = value;
599616
_numTypesValid = NR_DOUBLE;
617+
_numberIsNaN = true;
600618
return JsonToken.VALUE_NUMBER_FLOAT;
601619
}
602620

603621
@Override
604622
public boolean isNaN() throws IOException {
605-
if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
606-
if ((_numTypesValid & NR_DOUBLE) != 0) {
607-
return !Double.isFinite(_getNumberDouble());
608-
}
609-
}
610-
return false;
623+
// 01-Dec-2023, tatu: [core#1137] Only return explicit NaN
624+
return (_currToken == JsonToken.VALUE_NUMBER_FLOAT)
625+
&& _numberIsNaN;
611626
}
612627

613628
/*

src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,25 @@ public void testNegativeHexadecimal() throws Exception {
5858
}
5959
}
6060

61+
/**
62+
* Test for checking that Overflow for Double value does not lead
63+
* to bogus NaN information.
64+
*/
6165
public void testLargeDecimal() throws Exception {
62-
final String value = "7976931348623157e309";
66+
final String biggerThanDouble = "7976931348623157e309";
6367
for (int mode : ALL_MODES) {
64-
try (JsonParser p = createParser(mode, " " + value + " ")) {
68+
try (JsonParser p = createParser(mode, " " + biggerThanDouble + " ")) {
6569
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
66-
assertEquals(new BigDecimal(value), p.getDecimalValue());
70+
assertEquals(new BigDecimal(biggerThanDouble), p.getDecimalValue());
6771
assertFalse(p.isNaN());
6872
assertEquals(Double.POSITIVE_INFINITY, p.getValueAsDouble());
69-
// PJF: we might want to fix the isNaN check to not be affected by us reading the value as a double
70-
assertTrue(p.isNaN());
73+
// 01-Dec-2023, tatu: [core#1137] NaN only from explicit value
74+
assertFalse(p.isNaN());
7175
}
7276
}
7377
}
7478

75-
//JSON does not allow numbers to have f or d suffixes
79+
// JSON does not allow numbers to have f or d suffixes
7680
public void testFloatMarker() throws Exception {
7781
for (int mode : ALL_MODES) {
7882
try (JsonParser p = createParser(mode, " -0.123f ")) {

0 commit comments

Comments
 (0)