mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Implement reflective support for Java Records (#1381)
* Standardize around JDK 8 * Update GJF to support newer JDKs * Fix misc java 8 issues in tests * Prepare java 16/records checking at runtime * Implement real RecordJsonAdapter * Spotless * Prepare build for JDK 16+ * Fix property name for kapt * Small cleanup * Make FallbackEnum java-8 happy * Remove animalsniffer * Fix format * Add opens for ExtendsPlatformClassWithProtectedFields * Return null every time in shim for main tests * Use JDK 16 + release 8 to replace animalsniffer * Simplify accessor accessible handling * Remove manifest attrs * Fix typo * Fix KCT tests + upgrade it * Cover another * Try explicit kotlin daemon args for java 17? * Disable 17-ea for now until kotlin 1.5.30 * Add JsonQualifier and Json(name) tests + fix qualifiers * Ensure constructor is accessible * GJF it properly * GJF 1.11 * Unwrap InvocationTargetException * Use MethodHandle for constructor * Use MethodHandle for accessor too * Revert "Remove manifest attrs" This reverts commit 3eb768fd6904bb5c979aa01c3c182e0fb9329d62. * Proper MR jar * *actually* fix GJF, which wasn't getting applied before We can just enable this everywhere now since we require JDK 16 anyway * Make IDE happy about modules access * Fixup records tests to play nice with modules Gotta be public * Add complex smoke test * Remove comment Not a regression test in this case
This commit is contained in:
@@ -19,14 +19,49 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("com.vanniktech.maven.publish")
|
||||
id("ru.vyarus.animalsniffer")
|
||||
}
|
||||
|
||||
val mainSourceSet by sourceSets.named("main")
|
||||
val java16 by sourceSets.creating {
|
||||
java {
|
||||
srcDir("src/main/java16")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<JavaCompile>("compileJava16Java") {
|
||||
javaCompiler.set(
|
||||
javaToolchains.compilerFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(16))
|
||||
}
|
||||
)
|
||||
options.release.set(16)
|
||||
}
|
||||
|
||||
// Package our actual RecordJsonAdapter from java16 sources in and denote it as an MRJAR
|
||||
tasks.named<Jar>("jar") {
|
||||
from(java16.output) {
|
||||
into("META-INF/versions/16")
|
||||
}
|
||||
manifest {
|
||||
attributes("Multi-Release" to "true")
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
"java16Implementation" {
|
||||
extendsFrom(api.get())
|
||||
extendsFrom(implementation.get())
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
// ExtendsPlatformClassWithProtectedField tests a case where we set a protected ByteArrayOutputStream.buf field
|
||||
jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>()
|
||||
.configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.6"
|
||||
|
||||
if (name.contains("test", true)) {
|
||||
@Suppress("SuspiciousCollectionReassignment") // It's not suspicious
|
||||
freeCompilerArgs += listOf("-Xopt-in=kotlin.ExperimentalStdlibApi")
|
||||
@@ -35,6 +70,8 @@ tasks.withType<KotlinCompile>()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// So the j16 source set can "see" main Moshi sources
|
||||
"java16Implementation"(mainSourceSet.output)
|
||||
compileOnly(Dependencies.jsr305)
|
||||
api(Dependencies.okio)
|
||||
|
||||
|
30
moshi/records-tests/build.gradle.kts
Normal file
30
moshi/records-tests/build.gradle.kts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(16)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(project(":moshi"))
|
||||
testCompileOnly(Dependencies.jsr305)
|
||||
testImplementation(Dependencies.Testing.junit)
|
||||
testImplementation(Dependencies.Testing.truth)
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.records;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import com.squareup.moshi.FromJson;
|
||||
import com.squareup.moshi.Json;
|
||||
import com.squareup.moshi.JsonQualifier;
|
||||
import com.squareup.moshi.Moshi;
|
||||
import com.squareup.moshi.ToJson;
|
||||
import com.squareup.moshi.Types;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.junit.Test;
|
||||
|
||||
public final class RecordsTest {
|
||||
|
||||
private final Moshi moshi = new Moshi.Builder().build();
|
||||
|
||||
@Test
|
||||
public void smokeTest() throws IOException {
|
||||
var stringAdapter = moshi.adapter(String.class);
|
||||
var adapter =
|
||||
moshi
|
||||
.newBuilder()
|
||||
.add(CharSequence.class, stringAdapter)
|
||||
.add(Types.subtypeOf(CharSequence.class), stringAdapter)
|
||||
.add(Types.supertypeOf(CharSequence.class), stringAdapter)
|
||||
.build()
|
||||
.adapter(SmokeTestType.class);
|
||||
var instance =
|
||||
new SmokeTestType(
|
||||
"John",
|
||||
"Smith",
|
||||
25,
|
||||
List.of("American"),
|
||||
70.5f,
|
||||
null,
|
||||
true,
|
||||
List.of("super wildcards!"),
|
||||
List.of("extend wildcards!"),
|
||||
List.of("unbounded"),
|
||||
List.of("objectList"),
|
||||
new int[] {1, 2, 3},
|
||||
new String[] {"fav", "arrays"},
|
||||
Map.of("italian", "pasta"),
|
||||
Set.of(List.of(Map.of("someKey", new int[] {1}))),
|
||||
new Map[] {Map.of("Hello", "value")});
|
||||
var json = adapter.toJson(instance);
|
||||
var deserialized = adapter.fromJson(json);
|
||||
assertThat(deserialized).isEqualTo(instance);
|
||||
}
|
||||
|
||||
public static record SmokeTestType(
|
||||
@Json(name = "first_name") String firstName,
|
||||
@Json(name = "last_name") String lastName,
|
||||
int age,
|
||||
List<String> nationalities,
|
||||
float weight,
|
||||
Boolean tattoos, // Boxed primitive test
|
||||
boolean hasChildren,
|
||||
List<? super CharSequence> superWildcard,
|
||||
List<? extends CharSequence> extendsWildcard,
|
||||
List<?> unboundedWildcard,
|
||||
List<Object> objectList,
|
||||
int[] favoriteThreeNumbers,
|
||||
String[] favoriteArrayValues,
|
||||
Map<String, String> foodPreferences,
|
||||
Set<List<Map<String, int[]>>> setListMapArrayInt,
|
||||
Map<String, Object>[] nestedArray) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SmokeTestType that = (SmokeTestType) o;
|
||||
return age == that.age
|
||||
&& Float.compare(that.weight, weight) == 0
|
||||
&& hasChildren == that.hasChildren
|
||||
&& firstName.equals(that.firstName)
|
||||
&& lastName.equals(that.lastName)
|
||||
&& nationalities.equals(that.nationalities)
|
||||
&& Objects.equals(tattoos, that.tattoos)
|
||||
&& superWildcard.equals(that.superWildcard)
|
||||
&& extendsWildcard.equals(that.extendsWildcard)
|
||||
&& unboundedWildcard.equals(that.unboundedWildcard)
|
||||
&& objectList.equals(that.objectList)
|
||||
&& Arrays.equals(favoriteThreeNumbers, that.favoriteThreeNumbers)
|
||||
&& Arrays.equals(favoriteArrayValues, that.favoriteArrayValues)
|
||||
&& foodPreferences.equals(that.foodPreferences)
|
||||
// && setListMapArrayInt.equals(that.setListMapArrayInt) // Nested array equality doesn't
|
||||
// carry over
|
||||
&& Arrays.equals(nestedArray, that.nestedArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result =
|
||||
Objects.hash(
|
||||
firstName,
|
||||
lastName,
|
||||
age,
|
||||
nationalities,
|
||||
weight,
|
||||
tattoos,
|
||||
hasChildren,
|
||||
superWildcard,
|
||||
extendsWildcard,
|
||||
unboundedWildcard,
|
||||
objectList,
|
||||
foodPreferences,
|
||||
setListMapArrayInt);
|
||||
result = 31 * result + Arrays.hashCode(favoriteThreeNumbers);
|
||||
result = 31 * result + Arrays.hashCode(favoriteArrayValues);
|
||||
result = 31 * result + Arrays.hashCode(nestedArray);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericRecord() throws IOException {
|
||||
var adapter =
|
||||
moshi.<GenericRecord<String>>adapter(
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
RecordsTest.class, GenericRecord.class, String.class));
|
||||
assertThat(adapter.fromJson("{\"value\":\"Okay!\"}")).isEqualTo(new GenericRecord<>("Okay!"));
|
||||
}
|
||||
|
||||
public static record GenericRecord<T>(T value) {}
|
||||
|
||||
@Test
|
||||
public void genericBoundedRecord() throws IOException {
|
||||
var adapter =
|
||||
moshi.<GenericBoundedRecord<Integer>>adapter(
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
RecordsTest.class, GenericBoundedRecord.class, Integer.class));
|
||||
assertThat(adapter.fromJson("{\"value\":4}")).isEqualTo(new GenericBoundedRecord<>(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qualifiedValues() throws IOException {
|
||||
var adapter = moshi.newBuilder().add(new ColorAdapter()).build().adapter(QualifiedValues.class);
|
||||
assertThat(adapter.fromJson("{\"value\":\"#ff0000\"}"))
|
||||
.isEqualTo(new QualifiedValues(16711680));
|
||||
}
|
||||
|
||||
public static record QualifiedValues(@HexColor int value) {}
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@JsonQualifier
|
||||
@interface HexColor {}
|
||||
|
||||
/** Converts strings like #ff0000 to the corresponding color ints. */
|
||||
public static class ColorAdapter {
|
||||
@ToJson
|
||||
public String toJson(@HexColor int rgb) {
|
||||
return String.format("#%06x", rgb);
|
||||
}
|
||||
|
||||
@FromJson
|
||||
@HexColor
|
||||
public int fromJson(String rgb) {
|
||||
return Integer.parseInt(rgb.substring(1), 16);
|
||||
}
|
||||
}
|
||||
|
||||
public static record GenericBoundedRecord<T extends Number>(T value) {}
|
||||
|
||||
@Test
|
||||
public void jsonName() throws IOException {
|
||||
var adapter = moshi.adapter(JsonName.class);
|
||||
assertThat(adapter.fromJson("{\"actualValue\":3}")).isEqualTo(new JsonName(3));
|
||||
}
|
||||
|
||||
public static record JsonName(@Json(name = "actualValue") int value) {}
|
||||
}
|
@@ -51,6 +51,7 @@ public final class Moshi {
|
||||
BUILT_IN_FACTORIES.add(CollectionJsonAdapter.FACTORY);
|
||||
BUILT_IN_FACTORIES.add(MapJsonAdapter.FACTORY);
|
||||
BUILT_IN_FACTORIES.add(ArrayJsonAdapter.FACTORY);
|
||||
BUILT_IN_FACTORIES.add(RecordJsonAdapter.FACTORY);
|
||||
BUILT_IN_FACTORIES.add(ClassJsonAdapter.FACTORY);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This is just a simple shim for linking in {@link StandardJsonAdapters} and swapped with a real
|
||||
* implementation in Java 16 via MR Jar.
|
||||
*/
|
||||
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
||||
|
||||
static final JsonAdapter.Factory FACTORY =
|
||||
new JsonAdapter.Factory() {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JsonAdapter<?> create(
|
||||
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T fromJson(JsonReader reader) throws IOException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
218
moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java
Normal file
218
moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link JsonAdapter} that supports Java {@code record} classes via reflection.
|
||||
*
|
||||
* <p><em>NOTE:</em> Java records require JDK 16 or higher.
|
||||
*/
|
||||
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
||||
|
||||
static final JsonAdapter.Factory FACTORY =
|
||||
(type, annotations, moshi) -> {
|
||||
if (!annotations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var rawType = Types.getRawType(type);
|
||||
if (!rawType.isRecord()) {
|
||||
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 bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
||||
var constructorParams = new Class<?>[components.length];
|
||||
var lookup = MethodHandles.lookup();
|
||||
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
|
||||
RecordComponent component = components[i];
|
||||
constructorParams[i] = component.getType();
|
||||
var name = component.getName();
|
||||
var componentType = 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;
|
||||
Set<Annotation> qualifiers = null;
|
||||
for (var annotation : component.getDeclaredAnnotations()) {
|
||||
if (annotation instanceof Json jsonAnnotation) {
|
||||
jsonName = jsonAnnotation.name();
|
||||
} else {
|
||||
if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
|
||||
if (qualifiers == null) {
|
||||
qualifiers = new LinkedHashSet<>();
|
||||
}
|
||||
qualifiers.add(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (qualifiers == null) {
|
||||
qualifiers = Collections.emptySet();
|
||||
}
|
||||
var adapter = moshi.adapter(componentType, qualifiers);
|
||||
MethodHandle accessor;
|
||||
try {
|
||||
accessor = lookup.unreflect(component.getAccessor());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
var componentBinding = new ComponentBinding<>(name, jsonName, adapter, accessor);
|
||||
var replaced = bindings.put(jsonName, componentBinding);
|
||||
if (replaced != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Conflicting components:\n"
|
||||
+ " "
|
||||
+ replaced.name
|
||||
+ "\n"
|
||||
+ " "
|
||||
+ componentBinding.name);
|
||||
}
|
||||
}
|
||||
|
||||
MethodHandle constructor;
|
||||
try {
|
||||
constructor = lookup.findConstructor(rawType, methodType(void.class, constructorParams));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
|
||||
};
|
||||
|
||||
private static record ComponentBinding<T>(
|
||||
String name, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {}
|
||||
|
||||
private final String targetClass;
|
||||
private final MethodHandle constructor;
|
||||
private final ComponentBinding<Object>[] componentBindingsArray;
|
||||
private final JsonReader.Options options;
|
||||
|
||||
@SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument")
|
||||
public RecordJsonAdapter(
|
||||
MethodHandle constructor,
|
||||
String targetClass,
|
||||
Map<String, ComponentBinding<?>> componentBindings) {
|
||||
this.constructor = constructor;
|
||||
this.targetClass = targetClass;
|
||||
//noinspection unchecked
|
||||
this.componentBindingsArray =
|
||||
componentBindings.values().toArray(new ComponentBinding[componentBindings.size()]);
|
||||
this.options =
|
||||
JsonReader.Options.of(
|
||||
componentBindings.keySet().toArray(new String[componentBindings.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromJson(JsonReader reader) throws IOException {
|
||||
var resultsArray = new Object[componentBindingsArray.length];
|
||||
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
int index = reader.selectName(options);
|
||||
if (index == -1) {
|
||||
reader.skipName();
|
||||
reader.skipValue();
|
||||
continue;
|
||||
}
|
||||
var result = componentBindingsArray[index].adapter.fromJson(reader);
|
||||
resultsArray[index] = result;
|
||||
}
|
||||
reader.endObject();
|
||||
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (T) constructor.invokeWithArguments(resultsArray);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof InvocationTargetException ite) {
|
||||
Throwable cause = ite.getCause();
|
||||
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
|
||||
if (cause instanceof Error) throw (Error) cause;
|
||||
throw new RuntimeException(cause);
|
||||
} else {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(JsonWriter writer, T value) throws IOException {
|
||||
writer.beginObject();
|
||||
|
||||
for (var binding : componentBindingsArray) {
|
||||
writer.name(binding.jsonName);
|
||||
try {
|
||||
binding.adapter.toJson(writer, binding.accessor.invoke(value));
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof InvocationTargetException ite) {
|
||||
Throwable cause = ite.getCause();
|
||||
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
|
||||
if (cause instanceof Error) throw (Error) cause;
|
||||
throw new RuntimeException(cause);
|
||||
} else {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JsonAdapter(" + targetClass + ")";
|
||||
}
|
||||
}
|
@@ -126,7 +126,7 @@ public final class JsonAdapterTest {
|
||||
fail();
|
||||
} catch (JsonDataException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Unexpected null at $[1]");
|
||||
assertThat(reader.nextNull()).isNull();
|
||||
assertThat(reader.<Object>nextNull()).isNull();
|
||||
}
|
||||
assertThat(toUpperCase.fromJson(reader)).isEqualTo("C");
|
||||
reader.endArray();
|
||||
|
@@ -565,7 +565,7 @@ public final class JsonReaderTest {
|
||||
assertThat(reader2.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
|
||||
|
||||
JsonReader reader3 = newReader("null");
|
||||
assertThat(reader3.nextNull()).isNull();
|
||||
assertThat(reader3.<Object>nextNull()).isNull();
|
||||
assertThat(reader3.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
|
||||
|
||||
JsonReader reader4 = newReader("123");
|
||||
|
@@ -59,7 +59,7 @@ public final class JsonValueReaderTest {
|
||||
|
||||
assertThat(reader.hasNext()).isTrue();
|
||||
assertThat(reader.peek()).isEqualTo(JsonReader.Token.NULL);
|
||||
assertThat(reader.nextNull()).isNull();
|
||||
assertThat(reader.<Object>nextNull()).isNull();
|
||||
|
||||
assertThat(reader.hasNext()).isFalse();
|
||||
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_ARRAY);
|
||||
@@ -103,7 +103,7 @@ public final class JsonValueReaderTest {
|
||||
assertThat(reader.peek()).isEqualTo(JsonReader.Token.NAME);
|
||||
assertThat(reader.nextName()).isEqualTo("d");
|
||||
assertThat(reader.peek()).isEqualTo(JsonReader.Token.NULL);
|
||||
assertThat(reader.nextNull()).isNull();
|
||||
assertThat(reader.<Object>nextNull()).isNull();
|
||||
|
||||
assertThat(reader.hasNext()).isFalse();
|
||||
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_OBJECT);
|
||||
|
@@ -152,10 +152,10 @@ public final class JsonValueWriterTest {
|
||||
public void primitiveIntegerTypesEmitLong() throws Exception {
|
||||
JsonValueWriter writer = new JsonValueWriter();
|
||||
writer.beginArray();
|
||||
writer.value(new Byte(Byte.MIN_VALUE));
|
||||
writer.value(new Short(Short.MIN_VALUE));
|
||||
writer.value(new Integer(Integer.MIN_VALUE));
|
||||
writer.value(new Long(Long.MIN_VALUE));
|
||||
writer.value(Byte.valueOf(Byte.MIN_VALUE));
|
||||
writer.value(Short.valueOf(Short.MIN_VALUE));
|
||||
writer.value(Integer.valueOf(Integer.MIN_VALUE));
|
||||
writer.value(Long.valueOf(Long.MIN_VALUE));
|
||||
writer.endArray();
|
||||
|
||||
List<Number> numbers =
|
||||
@@ -167,8 +167,8 @@ public final class JsonValueWriterTest {
|
||||
public void primitiveFloatingPointTypesEmitDouble() throws Exception {
|
||||
JsonValueWriter writer = new JsonValueWriter();
|
||||
writer.beginArray();
|
||||
writer.value(new Float(0.5f));
|
||||
writer.value(new Double(0.5d));
|
||||
writer.value(Float.valueOf(0.5f));
|
||||
writer.value(Double.valueOf(0.5d));
|
||||
writer.endArray();
|
||||
|
||||
List<Number> numbers = Arrays.<Number>asList(0.5d, 0.5d);
|
||||
|
@@ -236,17 +236,17 @@ public final class JsonWriterTest {
|
||||
JsonWriter writer = factory.newWriter();
|
||||
writer.beginArray();
|
||||
try {
|
||||
writer.value(new Double(Double.NaN));
|
||||
writer.value(Double.valueOf(Double.NaN));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
writer.value(new Double(Double.NEGATIVE_INFINITY));
|
||||
writer.value(Double.valueOf(Double.NEGATIVE_INFINITY));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
writer.value(new Double(Double.POSITIVE_INFINITY));
|
||||
writer.value(Double.valueOf(Double.POSITIVE_INFINITY));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
|
Reference in New Issue
Block a user