diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index d0f8b793f8..fece6f0582 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -77,6 +77,8 @@ Project: jackson-databind (contributed by Carter K) #4694: Deserializing `BigDecimal` with large number of decimals result in incorrect value (reported by @lnthai2002) +#4699: Add extra `writeNumber()` method in `TokenBuffer` + (contributed by @pjfanning) 2.17.2 (05-Jul-2024) diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java index c5a0590c30..2ccb0b6d71 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java @@ -954,6 +954,27 @@ public void writeNumber(String encodedValue) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_FLOAT, encodedValue); } + /** + * Write method that can be used for custom numeric types that can + * not be (easily?) converted to "standard" Java number types. + * Because numbers are not surrounded by double quotes, regular + * {@link #writeString} method can not be used; nor + * {@link #writeRaw} because that does not properly handle + * value separators needed in Array or Object contexts. + * + * @param encodedValue Textual (possibly formatted) number representation to write + * @param isInteger Whether value should be considered an integer + * + * @throws IOException if there is either an underlying I/O problem or encoding + * issue at format layer + * @since 2.18 + */ + public void writeNumber(String encodedValue, boolean isInteger) throws IOException { + _appendValue( + isInteger ? JsonToken.VALUE_NUMBER_INT : JsonToken.VALUE_NUMBER_FLOAT, + encodedValue); + } + private void writeLazyInteger(Object encodedValue) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_INT, encodedValue); } @@ -1853,8 +1874,7 @@ public float getFloatValue() throws IOException { @Override public int getIntValue() throws IOException { - Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ? - ((Number) _currentObject()) : getNumberValue(); + final Number n = getNumberValue(false); if ((n instanceof Integer) || _smallerThanInt(n)) { return n.intValue(); } @@ -1863,8 +1883,7 @@ public int getIntValue() throws IOException @Override public long getLongValue() throws IOException { - Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ? - ((Number) _currentObject()) : getNumberValue(); + final Number n = getNumberValue(false); if ((n instanceof Long) || _smallerThanLong(n)) { return n.longValue(); } diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TokenBufferTest.java b/src/test/java/com/fasterxml/jackson/databind/util/TokenBufferTest.java index 6bc0689ba1..8be86ecc58 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/TokenBufferTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/TokenBufferTest.java @@ -236,6 +236,57 @@ public void testNumberOverflowLong() throws IOException } } + @Test + public void testNumberIntAsString() throws IOException + { + try (TokenBuffer buf = new TokenBuffer(null, false)) { + buf.writeNumber("123", true); + try (JsonParser p = buf.asParser()) { + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); + assertEquals(123, p.getIntValue()); + } + } + try (TokenBuffer buf = new TokenBuffer(null, false)) { + buf.writeNumber("-123", true); + try (JsonParser p = buf.asParser()) { + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); + assertEquals(-123L, p.getLongValue()); + } + } + try (TokenBuffer buf = new TokenBuffer(null, false)) { + buf.writeNumber("123", false); + try (JsonParser p = buf.asParser()) { + assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); + assertEquals(NumberType.BIG_DECIMAL, p.getNumberType()); + assertEquals(123.0, p.getFloatValue()); + } + } + // legacy method assumes Decimal type + try (TokenBuffer buf = new TokenBuffer(null, false)) { + buf.writeNumber("123"); + try (JsonParser p = buf.asParser()) { + assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); + assertEquals(NumberType.BIG_DECIMAL, p.getNumberType()); + assertEquals(123.0, p.getFloatValue()); + } + } + } + + @Test + public void testNumberNonIntAsStringNoCoerce() throws IOException + { + try (TokenBuffer buf = new TokenBuffer(null, false)) { + buf.writeNumber("1234.567", true); + try (JsonParser p = buf.asParser()) { + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); + assertThrows(NumberFormatException.class, p::getIntValue); + } + } + } + @Test public void testBigIntAsString() throws IOException {