From bf549f0589105a42f159c78a621fc28087561d16 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Sun, 9 Mar 2014 07:28:04 +0000 Subject: [PATCH] Added support for JsonAdapter annotation on fields --- .../google/gson/annotations/JsonAdapter.java | 2 +- ...onAdapterAnnotationTypeAdapterFactory.java | 12 ++++++--- .../bind/ReflectiveTypeAdapterFactory.java | 27 ++++++++++++++----- ...> JsonAdapterAnnotationOnClassesTest.java} | 8 +++--- 4 files changed, 34 insertions(+), 15 deletions(-) rename gson/src/test/java/com/google/gson/functional/{JsonAdapterAnnotationTest.java => JsonAdapterAnnotationOnClassesTest.java} (96%) diff --git a/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java index 1a12cfbd..093af47c 100644 --- a/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java +++ b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java @@ -72,7 +72,7 @@ import com.google.gson.TypeAdapter; */ // Note that the above example is taken from JsonAdapterANnotationTest. @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.FIELD}) public @interface JsonAdapter { Class> value(); 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 0f9d5fb9..7163fe29 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 @@ -43,9 +43,15 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte Class clazz = targetType.getRawType(); JsonAdapter annotation = clazz.getAnnotation(JsonAdapter.class); if (annotation == null) return null; - Class> adapterClass = annotation.value(); - ObjectConstructor> constructor = constructorConstructor.get(TypeToken.get(adapterClass)); - TypeAdapter adapter = constructor.construct(); + TypeAdapter adapter = getAnnotationTypeAdapter(constructorConstructor, annotation); return adapter; } + + static TypeAdapter getAnnotationTypeAdapter( + ConstructorConstructor constructorConstructor, JsonAdapter annotation) { + Class> adapterClass = annotation.value(); + ObjectConstructor> constructor = + constructorConstructor.get(TypeToken.get(adapterClass)); + return constructor.construct(); + } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java index e64ed17b..cc9631a3 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -16,11 +16,18 @@ package com.google.gson.internal.bind; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + import com.google.gson.FieldNamingStrategy; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; @@ -31,11 +38,6 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; /** * Type adapter that reflects over the fields and methods of a class. @@ -76,10 +78,9 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { final Gson context, final Field field, final String name, final TypeToken fieldType, boolean serialize, boolean deserialize) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); - // special casing primitives here saves ~5% on Android... return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { - final TypeAdapter typeAdapter = context.getAdapter(fieldType); + final TypeAdapter typeAdapter = getFieldAdapter(context, field, fieldType); @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree @Override void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException { @@ -98,6 +99,18 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { }; } + private TypeAdapter getFieldAdapter(Gson context, Field field, TypeToken fieldType) { + TypeAdapter adapter = context.getAdapter(fieldType); + // check if the registered adapter is a reflective type adapter. If so, JsonAdapter + // annotation should take precedence. Somewhat hackish, but works. + if (adapter instanceof Adapter && field.isAnnotationPresent(JsonAdapter.class)) { + JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); + return JsonAdapterAnnotationTypeAdapterFactory.getAnnotationTypeAdapter( + constructorConstructor, annotation); + } + return adapter; + } + private Map getBoundFields(Gson context, TypeToken type, Class raw) { Map result = new LinkedHashMap(); if (raw.isInterface()) { diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java similarity index 96% rename from gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationTest.java rename to gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java index b84dd4f9..bae36575 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java @@ -36,9 +36,9 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; /** - * Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation. + * Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation on classes. */ -public final class JsonAdapterAnnotationTest extends TestCase { +public final class JsonAdapterAnnotationOnClassesTest extends TestCase { public void testJsonAdapterInvoked() { Gson gson = new Gson(); @@ -154,8 +154,8 @@ public final class JsonAdapterAnnotationTest extends TestCase { // This class is used in JsonAdapter Javadoc as an example @JsonAdapter(UserJsonAdapter.class) private static class User { - public final String firstName, lastName; - private User(String firstName, String lastName) { + final String firstName, lastName; + User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; }