Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue where the parser can read back old number state when parsing later numbers #1391

Merged
merged 9 commits into from
Jan 27, 2025
7 changes: 6 additions & 1 deletion release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ a pure JSON library.
=== Releases ===
------------------------------------------------------------------------

2.17.4 (not yet released)

#1391: Fix issue where the parser can read back old number state when
parsing later numbers
(fix contributed by @pjfanning)

2.17.3 (01-Nov-2024)

#1331: Update to FastDoubleParser v1.0.1 to fix `BigDecimal` decoding problem
Expand All @@ -23,7 +29,6 @@ a pure JSON library.
#1352: Fix infinite loop due to integer overflow when reading large strings
(reported by Adam J.S)
(fix contributed by @pjfanning)


2.17.2 (05-Jul-2024)

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ public BigInteger getBigIntegerValue() throws IOException
}
if ((_numTypesValid & NR_BIGINT) == 0) {
convertNumberToBigInteger();
return _numberBigInt;
}
}
return _getBigInteger();
Expand All @@ -840,6 +841,7 @@ public float getFloatValue() throws IOException
}
if ((_numTypesValid & NR_FLOAT) == 0) {
convertNumberToFloat();
return _numberFloat;
}
}
return _getNumberFloat();
Expand All @@ -854,6 +856,7 @@ public double getDoubleValue() throws IOException
}
if ((_numTypesValid & NR_DOUBLE) == 0) {
convertNumberToDouble();
return _numberDouble;
}
}
return _getNumberDouble();
Expand All @@ -868,6 +871,7 @@ public BigDecimal getDecimalValue() throws IOException
}
if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
convertNumberToBigDecimal();
return _numberBigDecimal;
}
}
return _getBigDecimal();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.fasterxml.jackson.core.read;

import java.math.BigDecimal;
import java.math.BigInteger;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JUnit5TestBase;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TokenStreamFactory;

import static org.junit.jupiter.api.Assertions.assertEquals;

class NumberParsingDb4917Test extends JUnit5TestBase
{
private TokenStreamFactory JSON_F = newStreamFactory();

final String INPUT_JSON = a2q("{'decimalHolder':100.00,'number':50}");

// [jackson-databind#4917]
@Test
public void bigDecimal4917Integers() throws Exception
{
for (int mode : ALL_MODES) {
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.BIG_INTEGER);
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.INT);
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.LONG);
}
}

@Test
public void bigDecimal4917Floats() throws Exception
{
for (int mode : ALL_MODES) {
testBigDecimal4917(JSON_F, mode, INPUT_JSON, false, JsonParser.NumberType.DOUBLE);
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.DOUBLE);
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.FLOAT);
testBigDecimal4917(JSON_F, mode, INPUT_JSON, true, JsonParser.NumberType.BIG_DECIMAL);
}
}

private void testBigDecimal4917(final TokenStreamFactory jsonF,
final int mode,
final String json,
final boolean checkFirstNumValues,
final JsonParser.NumberType secondNumTypeCheck) throws Exception
{
// checkFirstNumValues=false reproduces the issue in https://github.com/FasterXML/jackson-databind/issues/4917
// it is useful to check the second number value while requesting different number types
// but the call adjusts state of the parser, so it is better to redo the test and then test w
try (JsonParser p = createParser(jsonF, mode, json)) {
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("decimalHolder", p.currentName());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertEquals(JsonParser.NumberType.DOUBLE, p.getNumberType());
if (checkFirstNumValues) {
assertEquals(Double.valueOf(100.0), p.getNumberValueDeferred());
assertEquals(new BigDecimal("100.00"), p.getDecimalValue());
}
assertEquals("100.00", p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("number", p.currentName());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(JsonParser.NumberType.INT, p.getNumberType());
assertEquals(Integer.valueOf(50), p.getNumberValueDeferred());
if (secondNumTypeCheck == JsonParser.NumberType.BIG_DECIMAL) {
assertEquals(new BigDecimal("50"), p.getDecimalValue());
} else if (secondNumTypeCheck == JsonParser.NumberType.BIG_INTEGER) {
assertEquals(new BigInteger("50"), p.getBigIntegerValue());
} else if (secondNumTypeCheck == JsonParser.NumberType.FLOAT) {
assertEquals(50.0f, p.getFloatValue());
} else if (secondNumTypeCheck == JsonParser.NumberType.LONG) {
assertEquals(50L, p.getLongValue());
} else if (secondNumTypeCheck == JsonParser.NumberType.INT) {
assertEquals(50, p.getIntValue());
} else {
assertEquals(50.0d, p.getDoubleValue());
}
assertEquals(50, p.getIntValue());
assertToken(JsonToken.END_OBJECT, p.nextToken());
}
}
}
Loading