diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java index fda2cf131c..7d18b6053b 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java @@ -25,6 +25,8 @@ import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -218,7 +220,10 @@ public JsonWriter value(Number value) throws IOException { return nullValue(); } - if (!isLenient()) { + // BigDecimal/BigInteger are always valid JSON numbers (mirrors + // JsonWriter.alwaysCreatesValidJsonNumber); their doubleValue() can overflow to Infinity + // even though the value is finite, so do not use it as the finiteness oracle for them. + if (!isLenient() && !(value instanceof BigDecimal) && !(value instanceof BigInteger)) { double d = value.doubleValue(); if (Double.isNaN(d) || Double.isInfinite(d)) { throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java index 3b8ad883e6..4137e2ff9b 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java @@ -27,6 +27,8 @@ import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Arrays; import java.util.List; import org.junit.Test; @@ -316,4 +318,36 @@ public void testEndArrayWhenStackTopIsNotArrayThrows() throws IOException { writer.beginObject(); assertThrows(IllegalStateException.class, () -> writer.endArray()); } + + @Test + public void testStrictWriterAcceptsFiniteBigDecimalAboveDoubleMax() throws IOException { + // Pre-fix, JsonTreeWriter.value(Number) routed Number arguments through + // doubleValue() for finiteness checking and rejected BigDecimal("1E400") — + // a finite value whose doubleValue() overflows to Infinity. JsonWriter + // already skips that check for BigDecimal/BigInteger via the + // alwaysCreatesValidJsonNumber whitelist; this test pins that JsonTreeWriter + // now mirrors that behavior in STRICT mode. + JsonTreeWriter writer = new JsonTreeWriter(); + writer.setStrictness(Strictness.STRICT); + writer.beginArray(); + writer.value(new BigDecimal("1E400")); + writer.endArray(); + assertThat(writer.get().toString()).isEqualTo("[1E+400]"); + } + + @Test + public void testStrictWriterAcceptsFiniteBigIntegerAboveDoubleMax() throws IOException { + // Same fix surface as the BigDecimal case above but for BigInteger. + // doubleValue() on a BigInteger > Double.MAX_VALUE overflows to Infinity; + // the alwaysCreatesValidJsonNumber whitelist covers both classes and the + // fix mirrors that. Pin BigInteger separately so a future change to the + // BigDecimal-only branch cannot silently regress BigInteger handling. + JsonTreeWriter writer = new JsonTreeWriter(); + writer.setStrictness(Strictness.STRICT); + BigInteger finiteHuge = BigInteger.TEN.pow(400); + writer.beginArray(); + writer.value(finiteHuge); + writer.endArray(); + assertThat(writer.get().toString()).isEqualTo("[" + finiteHuge + "]"); + } }