Added support for JsonAdapter annotation on fields
This commit is contained in:
parent
67d512ee7d
commit
bf549f0589
@ -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();
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user