Add TypeAdapterFactory support to @JsonAdapter.
This commit is contained in:
parent
125e6d9d3d
commit
117d8ea68f
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package com.google.gson.annotations;
|
package com.google.gson.annotations;
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import com.google.gson.TypeAdapter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class
|
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class
|
||||||
* or field.
|
* or field.
|
||||||
@ -87,6 +87,7 @@ import com.google.gson.TypeAdapter;
|
|||||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||||
public @interface JsonAdapter {
|
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.TypeAdapterFactory;
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import com.google.gson.internal.ConstructorConstructor;
|
import com.google.gson.internal.ConstructorConstructor;
|
||||||
import com.google.gson.internal.ObjectConstructor;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,8 +40,28 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
|
|||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||||
JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
|
JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
|
||||||
return annotation != null
|
if (annotation == null) {
|
||||||
? (TypeAdapter<T>) constructorConstructor.get(TypeToken.get(annotation.value())).construct()
|
return null;
|
||||||
: 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.LinkedHashMap;
|
||||||
import java.util.Map;
|
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.
|
* 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) {
|
private TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
|
||||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||||
return (annotation != null)
|
if (annotation != null) {
|
||||||
? constructorConstructor.get(TypeToken.get(annotation.value())).construct()
|
TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
|
||||||
: gson.getAdapter(fieldType);
|
if (adapter != null) return adapter;
|
||||||
|
}
|
||||||
|
return gson.getAdapter(fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
|
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
|
||||||
|
@ -16,11 +16,6 @@
|
|||||||
|
|
||||||
package com.google.gson.functional;
|
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.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
@ -31,9 +26,14 @@ import com.google.gson.JsonPrimitive;
|
|||||||
import com.google.gson.JsonSerializationContext;
|
import com.google.gson.JsonSerializationContext;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonWriter;
|
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.
|
* 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);
|
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() {
|
public void testRegisteredAdapterOverridesJsonAdapter() {
|
||||||
TypeAdapter<A> typeAdapter = new TypeAdapter<A>() {
|
TypeAdapter<A> typeAdapter = new TypeAdapter<A>() {
|
||||||
@Override public void write(JsonWriter out, A value) throws IOException {
|
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||||
@ -125,7 +133,7 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase {
|
|||||||
A(String value) {
|
A(String value) {
|
||||||
this.value = 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 {
|
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||||
out.value("jsonAdapter");
|
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 {
|
private static final class B extends A {
|
||||||
B(String value) {
|
B(String value) {
|
||||||
super(value);
|
super(value);
|
||||||
|
@ -19,7 +19,9 @@ package com.google.gson.functional;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -37,6 +39,14 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
|||||||
assertEquals("UserClassAnnotationAdapter", computer.user.name);
|
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() {
|
public void testRegisteredTypeAdapterTakesPrecedenceOverClassAnnotationAdapter() {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(User.class, new RegisteredUserAdapter())
|
.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 {
|
private static final class Part {
|
||||||
final String name;
|
final String name;
|
||||||
Part(String name) {
|
public Part(String name) {
|
||||||
this.name = 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 {
|
private static final class Computer {
|
||||||
final User user;
|
final User user;
|
||||||
Computer(User user) {
|
Computer(User user) {
|
||||||
|
Loading…
Reference in New Issue
Block a user