diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java index 13a7bb7e..d75e4ee0 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java @@ -55,6 +55,7 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct(); TypeAdapter typeAdapter; + boolean nullSafe = annotation.nullSafe(); if (instance instanceof TypeAdapter) { typeAdapter = (TypeAdapter) instance; } else if (instance instanceof TypeAdapterFactory) { @@ -66,7 +67,8 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte JsonDeserializer deserializer = instance instanceof JsonDeserializer ? (JsonDeserializer) instance : null; - typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null); + typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null, nullSafe); + nullSafe = false; } else { throw new IllegalArgumentException("Invalid attempt to bind an instance of " + instance.getClass().getName() + " as a @JsonAdapter for " + type.toString() @@ -74,7 +76,7 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte + " JsonSerializer or JsonDeserializer."); } - if (typeAdapter != null && annotation.nullSafe()) { + if (typeAdapter != null && nullSafe) { typeAdapter = typeAdapter.nullSafe(); } 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 50f46b5a..b7e92495 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 @@ -45,17 +45,24 @@ public final class TreeTypeAdapter extends TypeAdapter { private final TypeToken typeToken; private final TypeAdapterFactory skipPast; private final GsonContextImpl context = new GsonContextImpl(); + private final boolean nullSafe; /** The delegate is lazily created because it may not be needed, and creating it may fail. */ private volatile TypeAdapter delegate; public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, - Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast, boolean nullSafe) { this.serializer = serializer; this.deserializer = deserializer; this.gson = gson; this.typeToken = typeToken; this.skipPast = skipPast; + this.nullSafe = nullSafe; + } + + public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, + Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + this(serializer, deserializer, gson, typeToken, skipPast, true); } @Override public T read(JsonReader in) throws IOException { @@ -63,7 +70,7 @@ public final class TreeTypeAdapter extends TypeAdapter { return delegate().read(in); } JsonElement value = Streams.parse(in); - if (value.isJsonNull()) { + if (nullSafe && value.isJsonNull()) { return null; } return deserializer.deserialize(value, typeToken.getType(), context); @@ -74,7 +81,7 @@ public final class TreeTypeAdapter extends TypeAdapter { delegate().write(out, value); return; } - if (value == null) { + if (nullSafe && value == null) { out.nullValue(); return; } diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java index e6cb6dc8..f5398843 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java @@ -161,4 +161,22 @@ public final class JsonAdapterSerializerDeserializerTest extends TestCase { return new JsonPrimitive("BaseIntegerAdapter"); } } + + public void testJsonAdapterNullSafe() { + Gson gson = new Gson(); + String json = gson.toJson(new Computer3(null, null)); + assertEquals("{\"user1\":\"UserSerializerDeserializer\"}", json); + Computer3 computer3 = gson.fromJson("{\"user1\":null, \"user2\":null}", Computer3.class); + assertEquals("UserSerializerDeserializer", computer3.user1.name); + assertNull(computer3.user2); + } + + private static final class Computer3 { + @JsonAdapter(value = UserSerializerDeserializer.class, nullSafe = false) final User user1; + @JsonAdapter(value = UserSerializerDeserializer.class) final User user2; + Computer3(User user1, User user2) { + this.user1 = user1; + this.user2 = user2; + } + } }