From 9a8c249c5f3c428e581b2604af0ec241840feadf Mon Sep 17 00:00:00 2001 From: daguimu Date: Thu, 26 Mar 2026 09:56:11 +0800 Subject: [PATCH] fix: Validate value before writing deferred name in JsonWriter JsonWriter.value(float), value(double), and value(Number) called writeDeferredName() before validating whether the value is finite. This meant that if a NaN or Infinity value was rejected with an exception, the property name had already been written to the output, leaving the writer in an inconsistent state. Move the validation before writeDeferredName() so that invalid values are rejected without any side effects on the output. Fixes #1736 --- .../com/google/gson/stream/JsonWriter.java | 6 +-- .../google/gson/stream/JsonWriterTest.java | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index f08a698240..355f8f763b 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -576,10 +576,10 @@ public JsonWriter value(Boolean value) throws IOException { */ @CanIgnoreReturnValue public JsonWriter value(float value) throws IOException { - writeDeferredName(); if (strictness != Strictness.LENIENT && (Float.isNaN(value) || Float.isInfinite(value))) { throw new IllegalArgumentException("Numeric values must be finite, but was " + value); } + writeDeferredName(); beforeValue(); out.append(Float.toString(value)); return this; @@ -596,10 +596,10 @@ public JsonWriter value(float value) throws IOException { */ @CanIgnoreReturnValue public JsonWriter value(double value) throws IOException { - writeDeferredName(); if (strictness != Strictness.LENIENT && (Double.isNaN(value) || Double.isInfinite(value))) { throw new IllegalArgumentException("Numeric values must be finite, but was " + value); } + writeDeferredName(); beforeValue(); out.append(Double.toString(value)); return this; @@ -635,7 +635,6 @@ public JsonWriter value(Number value) throws IOException { return nullValue(); } - writeDeferredName(); String string = value.toString(); Class numberClass = value.getClass(); @@ -653,6 +652,7 @@ public JsonWriter value(Number value) throws IOException { } } + writeDeferredName(); beforeValue(); out.append(string); return this; diff --git a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java index fd171e880f..ab926ba9dc 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java @@ -423,6 +423,51 @@ public void testNonFiniteNumbersWhenLenient() throws IOException { assertThat(stringWriter.toString()).isEqualTo("[NaN,-Infinity,Infinity,Infinity]"); } + /** + * Regression test for https://github.com/google/gson/issues/1736 + * + *

When a non-finite value is rejected, the deferred name should not have been written yet, so + * the writer state remains consistent. + */ + @Test + public void testNonFiniteValueDoesNotWriteDeferredName() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(stringWriter); + jsonWriter.setSerializeNulls(false); + jsonWriter.beginObject(); + + // value(float) with NaN should not write the name + jsonWriter.name("a"); + assertThrows(IllegalArgumentException.class, () -> jsonWriter.value(Float.NaN)); + jsonWriter.nullValue(); // should be suppressed since serializeNulls=false + + // value(double) with NaN should not write the name + jsonWriter.name("b"); + assertThrows(IllegalArgumentException.class, () -> jsonWriter.value(Double.NaN)); + jsonWriter.nullValue(); + + // value(double) with Infinity should not write the name + jsonWriter.name("c"); + assertThrows(IllegalArgumentException.class, () -> jsonWriter.value(Double.POSITIVE_INFINITY)); + jsonWriter.nullValue(); + + // value(Number) with NaN should not write the name + jsonWriter.name("d"); + assertThrows( + IllegalArgumentException.class, () -> jsonWriter.value(Double.valueOf(Double.NaN))); + jsonWriter.nullValue(); + + // value(Number) with invalid number string should not write the name + jsonWriter.name("e"); + assertThrows( + IllegalArgumentException.class, + () -> jsonWriter.value(new LazilyParsedNumber("not-a-num"))); + jsonWriter.nullValue(); + + jsonWriter.endObject(); + assertThat(stringWriter.toString()).isEqualTo("{}"); + } + @Test public void testFloats() throws IOException { StringWriter stringWriter = new StringWriter();