diff --git a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java index 62ea14d383..2c662cb73f 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java @@ -20,6 +20,7 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; @@ -88,7 +89,15 @@ public T read(JsonReader in) throws IOException { if (deserializer == null) { return delegate().read(in); } - JsonElement value = Streams.parse(in); + JsonElement value; + try { + value = Streams.parse(in); + } catch (JsonIOException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } + throw e; + } if (nullSafe && value.isJsonNull()) { return null; } diff --git a/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java index 2d9efca61a..2d74a8571b 100644 --- a/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java @@ -17,19 +17,28 @@ package com.google.gson.functional; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -79,6 +88,55 @@ public void testDeserializeId() { assertThat(target.getId().getValue()).isEqualTo("1"); } + @Test + public void testReadPropagatesIOException() { + for (String prefix : Arrays.asList("", "{\"value\":")) { + IOException failure = new SocketTimeoutException("response timed out"); + Reader reader = + new StringReader(prefix) { + @Override + public int read(char[] buffer, int offset, int length) throws IOException { + int count = super.read(buffer, offset, length); + if (count == -1) { + throw failure; + } + return count; + } + }; + + TypeAdapter adapter = gson.getAdapter(Id.class); + IOException actual = assertThrows(IOException.class, () -> adapter.fromJson(reader)); + assertThat(actual).isSameInstanceAs(failure); + } + } + + @Test + public void testReadPreservesEOFExceptionAsJsonSyntaxException() { + TypeAdapter adapter = gson.getAdapter(Id.class); + Reader reader = new StringReader("["); + JsonSyntaxException actual = + assertThrows(JsonSyntaxException.class, () -> adapter.fromJson(reader)); + assertThat(actual).hasCauseThat().isInstanceOf(EOFException.class); + } + + @Test + public void testReadPreservesDeserializerException() { + JsonIOException failure = new JsonIOException(new IOException("custom deserializer")); + Gson customGson = + new GsonBuilder() + .registerTypeAdapter( + Id.class, + (JsonDeserializer>) + (json, type, context) -> { + throw failure; + }) + .create(); + + TypeAdapter adapter = customGson.getAdapter(Id.class); + JsonIOException actual = assertThrows(JsonIOException.class, () -> adapter.fromJson("1")); + assertThat(actual).isSameInstanceAs(failure); + } + @SuppressWarnings("UnusedTypeParameter") private static final class Id { final String value;