From 69321c90492b72b7ece38f683868ca6c47e78471 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 22 Jan 2025 21:28:03 +0100 Subject: [PATCH 1/6] try to fix issue where _numberString is used despite having old value --- .../com/fasterxml/jackson/core/base/ParserBase.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 73347e0405..a104ffc307 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -785,6 +785,7 @@ public int getIntValue() throws IOException } if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively? convertNumberToInt(); // let's make it so, if possible + _numberString = null; } } return _numberInt; @@ -799,6 +800,7 @@ public long getLongValue() throws IOException } if ((_numTypesValid & NR_LONG) == 0) { convertNumberToLong(); + _numberString = null; } } return _numberLong; @@ -813,6 +815,8 @@ public BigInteger getBigIntegerValue() throws IOException } if ((_numTypesValid & NR_BIGINT) == 0) { convertNumberToBigInteger(); + _numberString = null; + return _numberBigInt; } } return _getBigInteger(); @@ -835,6 +839,8 @@ public float getFloatValue() throws IOException } if ((_numTypesValid & NR_FLOAT) == 0) { convertNumberToFloat(); + _numberString = null; + return _numberFloat; } } return _getNumberFloat(); @@ -850,6 +856,8 @@ public double getDoubleValue() throws IOException // if underlying type not FP, need conversion: if ((_numTypesValid & NR_DOUBLE) == 0) { convertNumberToDouble(); + _numberString = null; + return _numberDouble; } } return _getNumberDouble(); @@ -864,6 +872,8 @@ public BigDecimal getDecimalValue() throws IOException } if ((_numTypesValid & NR_BIGDECIMAL) == 0) { convertNumberToBigDecimal(); + _numberString = null; + return _numberBigDecimal; } } return _getBigDecimal(); From fec1f38d5cfd520f1fc84e4c13822f0a14e6b6b0 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 23 Jan 2025 00:03:23 +0100 Subject: [PATCH 2/6] Create JsonNumberParsingTest.java --- .../core/read/JsonNumberParsingTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java new file mode 100644 index 0000000000..ff9de109b6 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.core.read; + +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 org.junit.jupiter.api.Test; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class JsonNumberParsingTest extends JUnit5TestBase +{ + // [jackson-databind#4917] + @Test + public void bigDecimal4917() throws Exception + { + final String json = a2q("{'decimalHolder':100.00,'number':50}"); + TokenStreamFactory jsonF = newStreamFactory(); + for (int mode : ALL_MODES) { + try (JsonParser p = createParser(jsonF, MODE_READER, 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(new BigDecimal("100.00"), p.getDecimalValue()); + assertToken(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("number", p.currentName()); + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(50.0, p.getDoubleValue()); + assertEquals(50, p.getIntValue()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); + } + } + } +} From d5dba39ebd56d14fdbbc27ca7bf6b6f7fabe2982 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 23 Jan 2025 11:06:11 +0100 Subject: [PATCH 3/6] Update JsonNumberParsingTest.java --- .../fasterxml/jackson/core/read/JsonNumberParsingTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java index ff9de109b6..ac465c6f3f 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java @@ -19,15 +19,18 @@ public void bigDecimal4917() throws Exception final String json = a2q("{'decimalHolder':100.00,'number':50}"); TokenStreamFactory jsonF = newStreamFactory(); for (int mode : ALL_MODES) { - try (JsonParser p = createParser(jsonF, MODE_READER, json)) { + 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("100.00", 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(Integer.valueOf(50), p.getNumberValueDeferred()); assertEquals(50.0, p.getDoubleValue()); assertEquals(50, p.getIntValue()); assertToken(JsonToken.END_OBJECT, p.nextToken()); From a1b3a0fed9c349ec74e58e7e0a8998169a017aea Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 23 Jan 2025 11:37:07 +0100 Subject: [PATCH 4/6] Update JsonNumberParsingTest.java --- .../fasterxml/jackson/core/read/JsonNumberParsingTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java index ac465c6f3f..2c0b957ed4 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java @@ -24,12 +24,14 @@ public void bigDecimal4917() throws Exception assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("decimalHolder", p.currentName()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); - assertEquals("100.00", p.getNumberValueDeferred()); + assertEquals(JsonParser.NumberType.DOUBLE, p.getNumberType()); + 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()); assertEquals(50.0, p.getDoubleValue()); assertEquals(50, p.getIntValue()); From 31b676ce26b3f8f60437d73811cbdec5d13e22d6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 23 Jan 2025 11:54:02 +0100 Subject: [PATCH 5/6] Update JsonNumberParsingTest.java --- .../core/read/JsonNumberParsingTest.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java index 2c0b957ed4..9e9a6d9a72 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.TokenStreamFactory; import org.junit.jupiter.api.Test; +import java.io.IOException; import java.math.BigDecimal; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -19,24 +20,35 @@ public void bigDecimal4917() throws Exception final String json = a2q("{'decimalHolder':100.00,'number':50}"); TokenStreamFactory jsonF = newStreamFactory(); for (int mode : ALL_MODES) { - 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()); + testBigDecimal4917(jsonF, mode, json, false); + testBigDecimal4917(jsonF, mode, json, true); + } + } + + private void testBigDecimal4917(final TokenStreamFactory jsonF, + final int mode, + final String json, + final boolean checkFirstNumValues) throws IOException { + // checkFirstNumValues=false reproduces the issue in https://github.com/FasterXML/jackson-databind/issues/4917 + 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()); - assertEquals(50.0, p.getDoubleValue()); - assertEquals(50, p.getIntValue()); - assertToken(JsonToken.END_OBJECT, p.nextToken()); } + 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()); + assertEquals(50.0, p.getDoubleValue()); + assertEquals(50, p.getIntValue()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); } } } From ac5b722b10f5b037ef80df53f0299b9066f36516 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 25 Jan 2025 15:28:48 +0100 Subject: [PATCH 6/6] Update JsonNumberParsingTest.java --- .../core/read/JsonNumberParsingTest.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java index 9e9a6d9a72..01eb4aa880 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/JsonNumberParsingTest.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -20,16 +21,24 @@ public void bigDecimal4917() throws Exception final String json = a2q("{'decimalHolder':100.00,'number':50}"); TokenStreamFactory jsonF = newStreamFactory(); for (int mode : ALL_MODES) { - testBigDecimal4917(jsonF, mode, json, false); - testBigDecimal4917(jsonF, mode, json, true); + testBigDecimal4917(jsonF, mode, json, false, JsonParser.NumberType.DOUBLE); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.DOUBLE); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.FLOAT); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.BIG_DECIMAL); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.BIG_INTEGER); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.INT); + testBigDecimal4917(jsonF, mode, json, true, JsonParser.NumberType.LONG); } } private void testBigDecimal4917(final TokenStreamFactory jsonF, final int mode, final String json, - final boolean checkFirstNumValues) throws IOException { + final boolean checkFirstNumValues, + final JsonParser.NumberType secondNumTypeCheck) throws IOException { // 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()); @@ -46,7 +55,19 @@ private void testBigDecimal4917(final TokenStreamFactory jsonF, assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonParser.NumberType.INT, p.getNumberType()); assertEquals(Integer.valueOf(50), p.getNumberValueDeferred()); - assertEquals(50.0, p.getDoubleValue()); + 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()); }