Fix ClassJsonAdapter to handle ParameterizedTypes. (#422)

This commit is contained in:
Eric Cochran
2018-02-17 19:56:05 -08:00
committed by Jesse Wilson
parent 834a401122
commit 3b89cf1fcb
2 changed files with 23 additions and 3 deletions

View File

@@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -46,8 +47,10 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!(type instanceof Class)) return null; if (!(type instanceof Class || type instanceof ParameterizedType)) {
Class<?> rawType = (Class<?>) type; return null;
}
Class<?> rawType = Types.getRawType(type);
if (rawType.isInterface() || rawType.isEnum()) return null; if (rawType.isInterface() || rawType.isEnum()) return null;
if (Util.isPlatformType(rawType) && !Types.isAllowedPlatformType(rawType)) { if (Util.isPlatformType(rawType) && !Types.isAllowedPlatformType(rawType)) {
throw new IllegalArgumentException("Platform " throw new IllegalArgumentException("Platform "
@@ -74,7 +77,7 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
ClassFactory<Object> classFactory = ClassFactory.get(rawType); ClassFactory<Object> classFactory = ClassFactory.get(rawType);
Map<String, FieldBinding<?>> fields = new TreeMap<>(); Map<String, FieldBinding<?>> fields = new TreeMap<>();
for (Type t = rawType; t != Object.class; t = Types.getGenericSuperclass(t)) { for (Type t = type; t != Object.class; t = Types.getGenericSuperclass(t)) {
createFieldBindings(moshi, t, fields); createFieldBindings(moshi, t, fields);
} }
return new ClassJsonAdapter<>(classFactory, fields).nullSafe(); return new ClassJsonAdapter<>(classFactory, fields).nullSafe();

View File

@@ -444,6 +444,23 @@ public final class ClassJsonAdapterTest {
assertThat(fromJson.zipCode).isEqualTo("94043"); assertThat(fromJson.zipCode).isEqualTo("94043");
} }
static final class Box<T> {
final T data;
Box(T data) {
this.data = data;
}
}
@Test public void parameterizedType() throws Exception {
@SuppressWarnings("unchecked")
JsonAdapter<Box<Integer>> adapter = (JsonAdapter<Box<Integer>>) ClassJsonAdapter.FACTORY.create(
Types.newParameterizedTypeWithOwner(ClassJsonAdapterTest.class, Box.class, Integer.class),
NO_ANNOTATIONS, moshi);
assertThat(adapter.fromJson("{\"data\":5}").data).isEqualTo(5);
assertThat(adapter.toJson(new Box<>(5))).isEqualTo("{\"data\":5}");
}
private <T> String toJson(Class<T> type, T value) throws IOException { private <T> String toJson(Class<T> type, T value) throws IOException {
@SuppressWarnings("unchecked") // Factory.create returns an adapter that matches its argument. @SuppressWarnings("unchecked") // Factory.create returns an adapter that matches its argument.
JsonAdapter<T> jsonAdapter = (JsonAdapter<T>) ClassJsonAdapter.FACTORY.create( JsonAdapter<T> jsonAdapter = (JsonAdapter<T>) ClassJsonAdapter.FACTORY.create(