Add TypeAdapterFactory support to @JsonAdapter.
This commit is contained in:
parent
125e6d9d3d
commit
117d8ea68f
@ -16,13 +16,13 @@
|
||||
|
||||
package com.google.gson.annotations;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
|
||||
/**
|
||||
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class
|
||||
* or field.
|
||||
@ -87,6 +87,7 @@ import com.google.gson.TypeAdapter;
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface JsonAdapter {
|
||||
|
||||
Class<? extends TypeAdapter<?>> value();
|
||||
/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}. */
|
||||
Class<?> value();
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
@ -41,8 +40,28 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||
JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
|
||||
return annotation != null
|
||||
? (TypeAdapter<T>) constructorConstructor.get(TypeToken.get(annotation.value())).construct()
|
||||
: null;
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // Casts guarded by conditionals.
|
||||
static TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
|
||||
TypeToken<?> fieldType, JsonAdapter annotation) {
|
||||
Class<?> value = annotation.value();
|
||||
if (TypeAdapter.class.isAssignableFrom(value)) {
|
||||
Class<TypeAdapter<?>> typeAdapter = (Class<TypeAdapter<?>>) value;
|
||||
return constructorConstructor.get(TypeToken.get(typeAdapter)).construct();
|
||||
}
|
||||
if (TypeAdapterFactory.class.isAssignableFrom(value)) {
|
||||
Class<TypeAdapterFactory> typeAdapterFactory = (Class<TypeAdapterFactory>) value;
|
||||
return constructorConstructor.get(TypeToken.get(typeAdapterFactory))
|
||||
.construct()
|
||||
.create(gson, fieldType);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference.");
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter;
|
||||
|
||||
/**
|
||||
* Type adapter that reflects over the fields and methods of a class.
|
||||
*/
|
||||
@ -100,9 +102,11 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
private TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
|
||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||
return (annotation != null)
|
||||
? constructorConstructor.get(TypeToken.get(annotation.value())).construct()
|
||||
: gson.getAdapter(fieldType);
|
||||
if (annotation != null) {
|
||||
TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
|
||||
if (adapter != null) return adapter;
|
||||
}
|
||||
return gson.getAdapter(fieldType);
|
||||
}
|
||||
|
||||
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
|
||||
|
@ -16,11 +16,6 @@
|
||||
|
||||
package com.google.gson.functional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
@ -31,9 +26,14 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation on classes.
|
||||
@ -53,6 +53,14 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase {
|
||||
assertEquals("Leitch", user.lastName);
|
||||
}
|
||||
|
||||
public void testJsonAdapterFactoryInvoked() {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(new C("bar"));
|
||||
assertEquals("\"jsonAdapterFactory\"", json);
|
||||
C c = gson.fromJson("\"bar\"", C.class);
|
||||
assertEquals("jsonAdapterFactory", c.value);
|
||||
}
|
||||
|
||||
public void testRegisteredAdapterOverridesJsonAdapter() {
|
||||
TypeAdapter<A> typeAdapter = new TypeAdapter<A>() {
|
||||
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||
@ -125,7 +133,7 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase {
|
||||
A(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
private static final class JsonAdapter extends TypeAdapter<A> {
|
||||
static final class JsonAdapter extends TypeAdapter<A> {
|
||||
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||
out.value("jsonAdapter");
|
||||
}
|
||||
@ -136,6 +144,27 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonAdapter(C.JsonAdapterFactory.class)
|
||||
private static class C {
|
||||
final String value;
|
||||
C(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
static final class JsonAdapterFactory implements TypeAdapterFactory {
|
||||
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||
return new TypeAdapter<T>() {
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
out.value("jsonAdapterFactory");
|
||||
}
|
||||
@Override public T read(JsonReader in) throws IOException {
|
||||
in.nextString();
|
||||
return (T) new C("jsonAdapterFactory");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class B extends A {
|
||||
B(String value) {
|
||||
super(value);
|
||||
|
@ -19,7 +19,9 @@ package com.google.gson.functional;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
@ -37,6 +39,14 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
||||
assertEquals("UserClassAnnotationAdapter", computer.user.name);
|
||||
}
|
||||
|
||||
public void testClassAnnotationAdapterFactoryTakesPrecedenceOverDefault() {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(new Gizmo(new Part("Part")));
|
||||
assertEquals("{\"part\":\"GizmoPartTypeAdapterFactory\"}", json);
|
||||
Gizmo computer = gson.fromJson("{'part':'Part'}", Gizmo.class);
|
||||
assertEquals("GizmoPartTypeAdapterFactory", computer.part.name);
|
||||
}
|
||||
|
||||
public void testRegisteredTypeAdapterTakesPrecedenceOverClassAnnotationAdapter() {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(User.class, new RegisteredUserAdapter())
|
||||
@ -80,9 +90,17 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Gizmo {
|
||||
@JsonAdapter(GizmoPartTypeAdapterFactory.class)
|
||||
final Part part;
|
||||
Gizmo(Part part) {
|
||||
this.part = part;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Part {
|
||||
final String name;
|
||||
Part(String name) {
|
||||
public Part(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@ -97,6 +115,21 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class GizmoPartTypeAdapterFactory implements TypeAdapterFactory {
|
||||
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||
return new TypeAdapter<T>() {
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
out.value("GizmoPartTypeAdapterFactory");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override public T read(JsonReader in) throws IOException {
|
||||
in.nextString();
|
||||
return (T) new Part("GizmoPartTypeAdapterFactory");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Computer {
|
||||
final User user;
|
||||
Computer(User user) {
|
||||
|
Loading…
Reference in New Issue
Block a user