From 011c870f749d6a5f0d0455e967937e2335e0a446 Mon Sep 17 00:00:00 2001 From: Lyubomyr Shaydariv Date: Fri, 30 Aug 2024 12:11:51 +0300 Subject: [PATCH] Add NullSafeTypeAdapter to prevent TypeAdapter.nullSafe() from returning nested null-safe type adapters (#2729) --- .../java/com/google/gson/TypeAdapter.java | 42 +++++++++------ .../java/com/google/gson/TypeAdapterTest.java | 51 ++++++++++++++----- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java index 90d330e382..3d9b2b9e45 100644 --- a/gson/src/main/java/com/google/gson/TypeAdapter.java +++ b/gson/src/main/java/com/google/gson/TypeAdapter.java @@ -289,24 +289,34 @@ public final T fromJsonTree(JsonElement jsonTree) { * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. */ public final TypeAdapter nullSafe() { - return new TypeAdapter() { - @Override - public void write(JsonWriter out, T value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - TypeAdapter.this.write(out, value); - } + if (!NullSafeTypeAdapter.class.isInstance(this)) { + return new NullSafeTypeAdapter(); + } + return this; + } + + private final class NullSafeTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, T value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + TypeAdapter.this.write(out, value); } + } - @Override - public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); - return null; - } - return TypeAdapter.this.read(reader); + @Override + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; } - }; + return TypeAdapter.this.read(reader); + } + + @Override + public String toString() { + return "NullSafeTypeAdapter[" + TypeAdapter.this + "]"; + } } } diff --git a/gson/src/test/java/com/google/gson/TypeAdapterTest.java b/gson/src/test/java/com/google/gson/TypeAdapterTest.java index e5aa8a422a..e95b029aa5 100644 --- a/gson/src/test/java/com/google/gson/TypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/TypeAdapterTest.java @@ -28,23 +28,50 @@ public class TypeAdapterTest { @Test public void testNullSafe() throws IOException { - TypeAdapter adapter = - new TypeAdapter() { - @Override - public void write(JsonWriter out, String value) { - throw new AssertionError("unexpected call"); - } - - @Override - public String read(JsonReader in) { - throw new AssertionError("unexpected call"); - } - }.nullSafe(); + TypeAdapter adapter = assertionErrorAdapter.nullSafe(); assertThat(adapter.toJson(null)).isEqualTo("null"); assertThat(adapter.fromJson("null")).isNull(); } + @Test + public void testNullSafe_ReturningSameInstanceOnceNullSafe() { + TypeAdapter nullSafeAdapter = assertionErrorAdapter.nullSafe(); + + assertThat(nullSafeAdapter.nullSafe()).isSameInstanceAs(nullSafeAdapter); + assertThat(nullSafeAdapter.nullSafe().nullSafe()).isSameInstanceAs(nullSafeAdapter); + assertThat(nullSafeAdapter.nullSafe().nullSafe().nullSafe()).isSameInstanceAs(nullSafeAdapter); + } + + @Test + public void testNullSafe_ToString() { + TypeAdapter adapter = assertionErrorAdapter; + + assertThat(adapter.toString()).isEqualTo("assertionErrorAdapter"); + assertThat(adapter.nullSafe().toString()) + .isEqualTo("NullSafeTypeAdapter[assertionErrorAdapter]"); + assertThat(adapter.nullSafe().nullSafe().toString()) + .isEqualTo("NullSafeTypeAdapter[assertionErrorAdapter]"); + } + + private static final TypeAdapter assertionErrorAdapter = + new TypeAdapter<>() { + @Override + public void write(JsonWriter out, String value) { + throw new AssertionError("unexpected call"); + } + + @Override + public String read(JsonReader in) { + throw new AssertionError("unexpected call"); + } + + @Override + public String toString() { + return "assertionErrorAdapter"; + } + }; + /** * Tests behavior when {@link TypeAdapter#write(JsonWriter, Object)} manually throws {@link * IOException} which is not caused by writer usage.