From 23e2916947940864f828505531aa130fba6d8743 Mon Sep 17 00:00:00 2001 From: Jerzy Chalupski Date: Mon, 18 Jan 2016 16:41:07 +0100 Subject: [PATCH] Make TypeAdapters created by @JsonAdapter null-safe --- ...onAdapterAnnotationTypeAdapterFactory.java | 18 ++++++++------- .../JsonAdapterAnnotationOnClassesTest.java | 23 +++++++++++++++++++ .../JsonAdapterAnnotationOnFieldsTest.java | 20 ++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) 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 77554e22..3801cfd4 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 @@ -51,18 +51,20 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte static TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson, TypeToken fieldType, JsonAdapter annotation) { Class value = annotation.value(); + final TypeAdapter typeAdapter; if (TypeAdapter.class.isAssignableFrom(value)) { - Class> typeAdapter = (Class>) value; - return constructorConstructor.get(TypeToken.get(typeAdapter)).construct(); - } - if (TypeAdapterFactory.class.isAssignableFrom(value)) { - Class typeAdapterFactory = (Class) value; - return constructorConstructor.get(TypeToken.get(typeAdapterFactory)) + Class> typeAdapterClass = (Class>) value; + typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterClass)).construct(); + } else if (TypeAdapterFactory.class.isAssignableFrom(value)) { + Class typeAdapterFactory = (Class) value; + typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterFactory)) .construct() .create(gson, fieldType); + } else { + throw new IllegalArgumentException( + "@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference."); } - throw new IllegalArgumentException( - "@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference."); + return typeAdapter.nullSafe(); } } diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java index d0798412..b7b29a65 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java @@ -133,6 +133,12 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase { assertFalse(json.contains("jsonAdapter")); } + public void testNullSafeObjectFromJson() { + Gson gson = new Gson(); + NullableClass fromJson = gson.fromJson("null", NullableClass.class); + assertNull(fromJson); + } + @JsonAdapter(A.JsonAdapter.class) private static class A { final String value; @@ -215,6 +221,23 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase { } } + @JsonAdapter(value = NullableClassJsonAdapter.class) + private static class NullableClass { + } + + private static class NullableClassJsonAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, NullableClass value) throws IOException { + out.value("nullable"); + } + + @Override + public NullableClass read(JsonReader in) throws IOException { + in.nextString(); + return new NullableClass(); + } + } + @JsonAdapter(FooJsonAdapter.class) private static enum Foo { BAR, BAZ } private static class FooJsonAdapter extends TypeAdapter { diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java index fb5121dc..4c745ec2 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java @@ -200,4 +200,24 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase { this.part2 = part2; } } + + public void testJsonAdapterWrappedInNullSafeAsRequested() { + Gson gson = new Gson(); + String fromJson = "{'part':null}"; + + GadgetWithOptionalPart gadget = gson.fromJson(fromJson, GadgetWithOptionalPart.class); + assertNull(gadget.part); + + String toJson = gson.toJson(gadget); + assertFalse(toJson.contains("PartJsonFieldAnnotationAdapter")); + } + + private static final class GadgetWithOptionalPart { + @JsonAdapter(value = PartJsonFieldAnnotationAdapter.class) + final Part part; + + private GadgetWithOptionalPart(Part part) { + this.part = part; + } + } }