mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
Fix a crash when combining generics and records (#1442)
Without the fix the test fails like so: No JsonAdapter for T (with no annotations) for T for java.util.List<T> for com.squareup.moshi.records.RecordsTest$IndirectGenerics<java.lang.Long> for class com.squareup.moshi.records.RecordsTest$HasIndirectGenerics java.lang.IllegalArgumentException: No JsonAdapter for T (with no annotations) for T for java.util.List<T> for com.squareup.moshi.records.RecordsTest$IndirectGenerics<java.lang.Long> for class com.squareup.moshi.records.RecordsTest$HasIndirectGenerics at com.squareup.moshi.Moshi$LookupChain.exceptionWithLookupStack(Moshi.java:389) at com.squareup.moshi.Moshi.adapter(Moshi.java:158) at com.squareup.moshi.Moshi.adapter(Moshi.java:106) at com.squareup.moshi.Moshi.adapter(Moshi.java:75)
This commit is contained in:
@@ -156,6 +156,21 @@ public final class RecordsTest {
|
|||||||
assertThat(adapter.fromJson("{\"value\":4}")).isEqualTo(new GenericBoundedRecord<>(4));
|
assertThat(adapter.fromJson("{\"value\":4}")).isEqualTo(new GenericBoundedRecord<>(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void indirectGenerics() throws IOException {
|
||||||
|
var value =
|
||||||
|
new HasIndirectGenerics(
|
||||||
|
new IndirectGenerics<>(1L, List.of(2L, 3L, 4L), Map.of("five", 5L)));
|
||||||
|
var jsonAdapter = moshi.adapter(HasIndirectGenerics.class);
|
||||||
|
var json = "{\"value\":{\"single\":1,\"list\":[2,3,4],\"map\":{\"five\":5}}}";
|
||||||
|
assertThat(jsonAdapter.toJson(value)).isEqualTo(json);
|
||||||
|
assertThat(jsonAdapter.fromJson(json)).isEqualTo(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static record IndirectGenerics<T>(T single, List<T> list, Map<String, T> map) {}
|
||||||
|
|
||||||
|
public static record HasIndirectGenerics(IndirectGenerics<Long> value) {}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void qualifiedValues() throws IOException {
|
public void qualifiedValues() throws IOException {
|
||||||
var adapter = moshi.newBuilder().add(new ColorAdapter()).build().adapter(QualifiedValues.class);
|
var adapter = moshi.newBuilder().add(new ColorAdapter()).build().adapter(QualifiedValues.class);
|
||||||
|
@@ -17,6 +17,7 @@ package com.squareup.moshi;
|
|||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
|
|
||||||
|
import com.squareup.moshi.internal.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
@@ -24,8 +25,6 @@ import java.lang.invoke.MethodHandles;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.RecordComponent;
|
import java.lang.reflect.RecordComponent;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.lang.reflect.TypeVariable;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -54,39 +53,15 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Type> mappedTypeArgs = null;
|
|
||||||
if (type instanceof ParameterizedType parameterizedType) {
|
|
||||||
Type[] typeArgs = parameterizedType.getActualTypeArguments();
|
|
||||||
var typeVars = rawType.getTypeParameters();
|
|
||||||
mappedTypeArgs = new LinkedHashMap<>(typeArgs.length);
|
|
||||||
for (int i = 0; i < typeArgs.length; ++i) {
|
|
||||||
var typeVarName = typeVars[i].getName();
|
|
||||||
var materialized = typeArgs[i];
|
|
||||||
mappedTypeArgs.put(typeVarName, materialized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var components = rawType.getRecordComponents();
|
var components = rawType.getRecordComponents();
|
||||||
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
||||||
var constructorParams = new Class<?>[components.length];
|
var componentRawTypes = new Class<?>[components.length];
|
||||||
var lookup = MethodHandles.lookup();
|
var lookup = MethodHandles.lookup();
|
||||||
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
|
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
|
||||||
RecordComponent component = components[i];
|
RecordComponent component = components[i];
|
||||||
constructorParams[i] = component.getType();
|
componentRawTypes[i] = component.getType();
|
||||||
var name = component.getName();
|
var name = component.getName();
|
||||||
var componentType = component.getGenericType();
|
var componentType = Util.resolve(type, rawType, component.getGenericType());
|
||||||
if (componentType instanceof TypeVariable<?> typeVariable) {
|
|
||||||
var typeVarName = typeVariable.getName();
|
|
||||||
if (mappedTypeArgs == null) {
|
|
||||||
throw new AssertionError(
|
|
||||||
"No mapped type arguments found for type '" + typeVarName + "'");
|
|
||||||
}
|
|
||||||
var mappedType = mappedTypeArgs.get(typeVarName);
|
|
||||||
if (mappedType == null) {
|
|
||||||
throw new AssertionError(
|
|
||||||
"No materialized type argument found for type '" + typeVarName + "'");
|
|
||||||
}
|
|
||||||
componentType = mappedType;
|
|
||||||
}
|
|
||||||
var jsonName = name;
|
var jsonName = name;
|
||||||
Set<Annotation> qualifiers = null;
|
Set<Annotation> qualifiers = null;
|
||||||
for (var annotation : component.getDeclaredAnnotations()) {
|
for (var annotation : component.getDeclaredAnnotations()) {
|
||||||
@@ -129,7 +104,7 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
|
|
||||||
MethodHandle constructor;
|
MethodHandle constructor;
|
||||||
try {
|
try {
|
||||||
constructor = lookup.findConstructor(rawType, methodType(void.class, constructorParams));
|
constructor = lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user