mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 16:09:21 +08:00
Support covariant Map values, which are used by Kotlin
This commit is contained in:
@@ -34,6 +34,7 @@ import javax.annotation.CheckReturnValue;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.squareup.moshi.internal.Util.canonicalize;
|
||||
import static com.squareup.moshi.internal.Util.removeSubtypeWildcard;
|
||||
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
|
||||
|
||||
/**
|
||||
@@ -112,7 +113,7 @@ public final class Moshi {
|
||||
throw new NullPointerException("annotations == null");
|
||||
}
|
||||
|
||||
type = canonicalize(type);
|
||||
type = removeSubtypeWildcard(canonicalize(type));
|
||||
|
||||
// If there's an equivalent adapter in the cache, we're done!
|
||||
Object cacheKey = cacheKey(type, annotations);
|
||||
@@ -158,7 +159,7 @@ public final class Moshi {
|
||||
Set<? extends Annotation> annotations) {
|
||||
if (annotations == null) throw new NullPointerException("annotations == null");
|
||||
|
||||
type = canonicalize(type);
|
||||
type = removeSubtypeWildcard(canonicalize(type));
|
||||
|
||||
int skipPastIndex = factories.indexOf(skipPast);
|
||||
if (skipPastIndex == -1) {
|
||||
|
@@ -139,6 +139,21 @@ public final class Util {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged.
|
||||
*/
|
||||
public static Type removeSubtypeWildcard(Type type) {
|
||||
if (!(type instanceof WildcardType)) return type;
|
||||
|
||||
Type[] lowerBounds = ((WildcardType) type).getLowerBounds();
|
||||
if (lowerBounds.length != 0) return type;
|
||||
|
||||
Type[] upperBounds = ((WildcardType) type).getUpperBounds();
|
||||
if (upperBounds.length != 1) throw new IllegalArgumentException();
|
||||
|
||||
return upperBounds[0];
|
||||
}
|
||||
|
||||
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
|
||||
// This implementation is made a little more complicated in an attempt to avoid object-creation.
|
||||
while (true) {
|
||||
|
@@ -86,6 +86,27 @@ public final class MapJsonAdapterTest {
|
||||
assertThat(jsonAdapter.fromJson(jsonReader)).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test public void covariantValue() throws Exception {
|
||||
// Important for Kotlin maps, which are all Map<K, ? extends T>.
|
||||
JsonAdapter<Map<String, Object>> jsonAdapter =
|
||||
mapAdapter(String.class, Types.subtypeOf(Object.class));
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("boolean", true);
|
||||
map.put("float", 42.0);
|
||||
map.put("String", "value");
|
||||
|
||||
String asJson = "{\"boolean\":true,\"float\":42.0,\"String\":\"value\"}";
|
||||
|
||||
Buffer buffer = new Buffer();
|
||||
JsonWriter jsonWriter = JsonWriter.of(buffer);
|
||||
jsonAdapter.toJson(jsonWriter, map);
|
||||
assertThat(buffer.readUtf8()).isEqualTo(asJson);
|
||||
|
||||
JsonReader jsonReader = newReader(asJson);
|
||||
assertThat(jsonAdapter.fromJson(jsonReader)).isEqualTo(map);
|
||||
}
|
||||
|
||||
@Test public void orderIsRetained() throws Exception {
|
||||
Map<String, Integer> map = new LinkedHashMap<>();
|
||||
map.put("c", 1);
|
||||
|
@@ -537,15 +537,13 @@ public final class MoshiTest {
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test public void upperBoundedWildcardsAreNotHandled() {
|
||||
@Test public void upperBoundedWildcardsAreHandled() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
try {
|
||||
moshi.adapter(Types.subtypeOf(String.class));
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage(
|
||||
"No JsonAdapter for ? extends java.lang.String (with no annotations)");
|
||||
}
|
||||
JsonAdapter<Object> adapter = moshi.adapter(Types.subtypeOf(String.class));
|
||||
assertThat(adapter.fromJson("\"a\"")).isEqualTo("a");
|
||||
assertThat(adapter.toJson("b")).isEqualTo("\"b\"");
|
||||
assertThat(adapter.fromJson("null")).isEqualTo(null);
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test public void lowerBoundedWildcardsAreNotHandled() {
|
||||
|
Reference in New Issue
Block a user