diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index a06f290747..78880bc060 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -979,7 +979,7 @@ public T fromJson(Reader json, TypeToken typeOfT) throws JsonIOException, JsonSyntaxException { JsonReader jsonReader = newJsonReader(json); T object = fromJson(jsonReader, typeOfT); - assertFullConsumption(object, jsonReader); + assertFullConsumption(jsonReader); return object; } @@ -1193,11 +1193,14 @@ public T fromJson(JsonElement json, TypeToken typeOfT) throws JsonSyntaxE return fromJson(new JsonTreeReader(json), typeOfT); } - private static void assertFullConsumption(Object obj, JsonReader reader) { + private static void assertFullConsumption(JsonReader reader) { try { - if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { + if (reader.peek() != JsonToken.END_DOCUMENT) { throw new JsonSyntaxException("JSON document was not fully consumed."); } + } catch (EOFException e) { + // Stream is exhausted — no trailing data. This happens when the input was empty + // or contained only whitespace, which Gson accepts for backward compatibility. } catch (MalformedJsonException e) { throw new JsonSyntaxException(e); } catch (IOException e) { diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java index d56da87a48..e683fcafd3 100644 --- a/gson/src/main/java/com/google/gson/JsonParser.java +++ b/gson/src/main/java/com/google/gson/JsonParser.java @@ -20,6 +20,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.MalformedJsonException; +import java.io.EOFException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; @@ -108,8 +109,13 @@ public static JsonElement parseReader(Reader reader) throws JsonIOException, Jso try { JsonReader jsonReader = new JsonReader(reader); JsonElement element = parseReader(jsonReader); - if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) { - throw new JsonSyntaxException("Did not consume the entire document."); + try { + if (jsonReader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonSyntaxException("Did not consume the entire document."); + } + } catch (EOFException e) { + // Stream is exhausted — no trailing data. This happens when the input was empty + // or contained only whitespace, which Gson accepts for backward compatibility. } return element; } catch (MalformedJsonException | NumberFormatException e) { diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index 2d00dd4d35..e7b2290788 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -637,4 +637,28 @@ public CustomClass3() { this(NO_ARG_CONSTRUCTOR_VALUE); } } + + /** + * Gson.fromJson should reject trailing data after a top-level JSON null, just as it does for + * non-null values. Previously, assertFullConsumption skipped the reader.peek() check when the + * deserialized object was null, silently ignoring any trailing content. + */ + @Test + public void testFromJsonRejectsTrailingDataAfterNull_String() { + Gson gson = new Gson(); + // Non-null with trailing data already throws — verify that baseline + assertThrows( + JsonSyntaxException.class, () -> gson.fromJson("\"hello\" trailing", String.class)); + // Null with trailing data should also throw, but previously didn't + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("null trailing", String.class)); + } + + @Test + public void testFromJsonRejectsTrailingDataAfterNull_Reader() { + Gson gson = new Gson(); + // Null with trailing data through the Reader overload + assertThrows( + JsonSyntaxException.class, + () -> gson.fromJson(new StringReader("null trailing"), String.class)); + } } diff --git a/gson/src/test/java/com/google/gson/JsonParserTest.java b/gson/src/test/java/com/google/gson/JsonParserTest.java index 47192c797b..f193c836a3 100644 --- a/gson/src/test/java/com/google/gson/JsonParserTest.java +++ b/gson/src/test/java/com/google/gson/JsonParserTest.java @@ -195,4 +195,20 @@ public void testStrict() { // Original strictness was kept assertThat(reader.getStrictness()).isEqualTo(strictness); } + + /** + * JsonParser.parseReader(Reader) should reject trailing data after a top-level JSON null, just as + * it does for non-null values. Previously, the trailing-data check was gated by + * !element.isJsonNull(), silently ignoring any content after "null". + */ + @Test + public void testParseReaderRejectsTrailingDataAfterNull() { + // Non-null with trailing data already throws — verify that baseline + assertThrows( + JsonSyntaxException.class, + () -> JsonParser.parseReader(new StringReader("\"hello\" trailing"))); + // Null with trailing data should also throw, but previously didn't + assertThrows( + JsonSyntaxException.class, () -> JsonParser.parseReader(new StringReader("null trailing"))); + } }