Added support for JsonAdapter annotation on fields

This commit is contained in:
Inderjeet Singh 2014-03-09 07:28:04 +00:00
parent 67d512ee7d
commit bf549f0589
4 changed files with 34 additions and 15 deletions

View File

@ -72,7 +72,7 @@ import com.google.gson.TypeAdapter;
*/ */
// Note that the above example is taken from JsonAdapterANnotationTest. // Note that the above example is taken from JsonAdapterANnotationTest.
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonAdapter { public @interface JsonAdapter {
Class<? extends TypeAdapter<?>> value(); Class<? extends TypeAdapter<?>> value();

View File

@ -43,9 +43,15 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
Class<? super T> clazz = targetType.getRawType(); Class<? super T> clazz = targetType.getRawType();
JsonAdapter annotation = clazz.getAnnotation(JsonAdapter.class); JsonAdapter annotation = clazz.getAnnotation(JsonAdapter.class);
if (annotation == null) return null; if (annotation == null) return null;
Class<? extends TypeAdapter<?>> adapterClass = annotation.value(); TypeAdapter adapter = getAnnotationTypeAdapter(constructorConstructor, annotation);
ObjectConstructor<? extends TypeAdapter<?>> constructor = constructorConstructor.get(TypeToken.get(adapterClass));
TypeAdapter adapter = constructor.construct();
return adapter; return adapter;
} }
static TypeAdapter<?> getAnnotationTypeAdapter(
ConstructorConstructor constructorConstructor, JsonAdapter annotation) {
Class<? extends TypeAdapter<?>> adapterClass = annotation.value();
ObjectConstructor<? extends TypeAdapter<?>> constructor =
constructorConstructor.get(TypeToken.get(adapterClass));
return constructor.construct();
}
} }

View File

@ -16,11 +16,18 @@
package com.google.gson.internal.bind; 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.FieldNamingStrategy;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory; import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.$Gson$Types;
import com.google.gson.internal.ConstructorConstructor; 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.JsonReader;
import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter; 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. * 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 Gson context, final Field field, final String name,
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) { final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
// special casing primitives here saves ~5% on Android... // special casing primitives here saves ~5% on Android...
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { 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 @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
@Override void write(JsonWriter writer, Object value) @Override void write(JsonWriter writer, Object value)
throws IOException, IllegalAccessException { 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<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) { private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>(); Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) { if (raw.isInterface()) {

View File

@ -36,9 +36,9 @@ import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; 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() { public void testJsonAdapterInvoked() {
Gson gson = new Gson(); Gson gson = new Gson();
@ -154,8 +154,8 @@ public final class JsonAdapterAnnotationTest extends TestCase {
// This class is used in JsonAdapter Javadoc as an example // This class is used in JsonAdapter Javadoc as an example
@JsonAdapter(UserJsonAdapter.class) @JsonAdapter(UserJsonAdapter.class)
private static class User { private static class User {
public final String firstName, lastName; final String firstName, lastName;
private User(String firstName, String lastName) { User(String firstName, String lastName) {
this.firstName = firstName; this.firstName = firstName;
this.lastName = lastName; this.lastName = lastName;
} }