diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 694d808376..ec4d6b89f6 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -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 @@ -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) diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index e2d80de125..4b09c42e54 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -818,6 +818,7 @@ public BigInteger getBigIntegerValue() throws IOException } if ((_numTypesValid & NR_BIGINT) == 0) { convertNumberToBigInteger(); + return _numberBigInt; } } return _getBigInteger(); @@ -840,6 +841,7 @@ public float getFloatValue() throws IOException } if ((_numTypesValid & NR_FLOAT) == 0) { convertNumberToFloat(); + return _numberFloat; } } return _getNumberFloat(); @@ -854,6 +856,7 @@ public double getDoubleValue() throws IOException } if ((_numTypesValid & NR_DOUBLE) == 0) { convertNumberToDouble(); + return _numberDouble; } } return _getNumberDouble(); @@ -868,6 +871,7 @@ public BigDecimal getDecimalValue() throws IOException } if ((_numTypesValid & NR_BIGDECIMAL) == 0) { convertNumberToBigDecimal(); + return _numberBigDecimal; } } return _getBigDecimal(); diff --git a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingDb4917Test.java b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingDb4917Test.java new file mode 100644 index 0000000000..7e6dd7423f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingDb4917Test.java @@ -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()); + } + } +}