Skip to content

Commit

Permalink
Add NullSafeTypeAdapter to prevent TypeAdapter.nullSafe()
Browse files Browse the repository at this point in the history
from returning nested null-safe type adapters (#2729)
  • Loading branch information
lyubomyr-shaydariv committed Sep 2, 2024
1 parent 93596da commit 011c870
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 28 deletions.
42 changes: 26 additions & 16 deletions gson/src/main/java/com/google/gson/TypeAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> nullSafe() {
return new TypeAdapter<T>() {
@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<T> {
@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 + "]";
}
}
}
51 changes: 39 additions & 12 deletions gson/src/test/java/com/google/gson/TypeAdapterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,50 @@
public class TypeAdapterTest {
@Test
public void testNullSafe() throws IOException {
TypeAdapter<String> adapter =
new TypeAdapter<String>() {
@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<String> 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<String> 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.
Expand Down

0 comments on commit 011c870

Please sign in to comment.