mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Use functions in Util where possible (#1444)
This commit is contained in:
@@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi.adapters;
|
package com.squareup.moshi.adapters;
|
||||||
|
|
||||||
import com.squareup.moshi.Json;
|
import static com.squareup.moshi.internal.Util.jsonName;
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
import com.squareup.moshi.JsonAdapter;
|
||||||
import com.squareup.moshi.JsonDataException;
|
import com.squareup.moshi.JsonDataException;
|
||||||
import com.squareup.moshi.JsonReader;
|
import com.squareup.moshi.JsonReader;
|
||||||
@@ -67,12 +68,7 @@ public final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
|
|||||||
nameStrings = new String[constants.length];
|
nameStrings = new String[constants.length];
|
||||||
for (int i = 0; i < constants.length; i++) {
|
for (int i = 0; i < constants.length; i++) {
|
||||||
String constantName = constants[i].name();
|
String constantName = constants[i].name();
|
||||||
Json annotation = enumType.getField(constantName).getAnnotation(Json.class);
|
nameStrings[i] = jsonName(constantName, enumType.getField(constantName));
|
||||||
String name =
|
|
||||||
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
|
|
||||||
? annotation.name()
|
|
||||||
: constantName;
|
|
||||||
nameStrings[i] = name;
|
|
||||||
}
|
}
|
||||||
options = JsonReader.Options.of(nameStrings);
|
options = JsonReader.Options.of(nameStrings);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
|
@@ -17,13 +17,13 @@ package com.squareup.moshi.recipes;
|
|||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
import com.squareup.moshi.Json;
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
import com.squareup.moshi.JsonAdapter;
|
||||||
import com.squareup.moshi.JsonQualifier;
|
import com.squareup.moshi.JsonQualifier;
|
||||||
import com.squareup.moshi.JsonReader;
|
import com.squareup.moshi.JsonReader;
|
||||||
import com.squareup.moshi.JsonWriter;
|
import com.squareup.moshi.JsonWriter;
|
||||||
import com.squareup.moshi.Moshi;
|
import com.squareup.moshi.Moshi;
|
||||||
import com.squareup.moshi.Types;
|
import com.squareup.moshi.Types;
|
||||||
|
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.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -77,13 +77,8 @@ final class FallbackEnum {
|
|||||||
constants = enumType.getEnumConstants();
|
constants = enumType.getEnumConstants();
|
||||||
nameStrings = new String[constants.length];
|
nameStrings = new String[constants.length];
|
||||||
for (int i = 0; i < constants.length; i++) {
|
for (int i = 0; i < constants.length; i++) {
|
||||||
T constant = constants[i];
|
String constantName = constants[i].name();
|
||||||
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
|
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
|
||||||
String name =
|
|
||||||
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
|
|
||||||
? annotation.name()
|
|
||||||
: constant.name();
|
|
||||||
nameStrings[i] = name;
|
|
||||||
}
|
}
|
||||||
options = JsonReader.Options.of(nameStrings);
|
options = JsonReader.Options.of(nameStrings);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
|
@@ -135,7 +135,7 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
for (binding in allBindings) {
|
for (binding in allBindings) {
|
||||||
if (binding == null) continue // Skip constructor parameters that aren't properties.
|
if (binding == null) continue // Skip constructor parameters that aren't properties.
|
||||||
|
|
||||||
writer.name(binding.name)
|
writer.name(binding.jsonName)
|
||||||
binding.adapter.toJson(writer, binding.get(value))
|
binding.adapter.toJson(writer, binding.get(value))
|
||||||
}
|
}
|
||||||
writer.endObject()
|
writer.endObject()
|
||||||
@@ -144,8 +144,7 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
override fun toString() = "KotlinJsonAdapter(${constructor.returnType})"
|
override fun toString() = "KotlinJsonAdapter(${constructor.returnType})"
|
||||||
|
|
||||||
data class Binding<K, P>(
|
data class Binding<K, P>(
|
||||||
val name: String,
|
val jsonName: String,
|
||||||
val jsonName: String?,
|
|
||||||
val adapter: JsonAdapter<P>,
|
val adapter: JsonAdapter<P>,
|
||||||
val property: KProperty1<K, P>,
|
val property: KProperty1<K, P>,
|
||||||
val parameter: KParameter?,
|
val parameter: KParameter?,
|
||||||
@@ -264,7 +263,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
|||||||
|
|
||||||
if (property !is KMutableProperty1 && parameter == null) continue
|
if (property !is KMutableProperty1 && parameter == null) continue
|
||||||
|
|
||||||
val name = jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: property.name
|
val jsonName = jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: property.name
|
||||||
val propertyType = when (val propertyTypeClassifier = property.returnType.classifier) {
|
val propertyType = when (val propertyTypeClassifier = property.returnType.classifier) {
|
||||||
is KClass<*> -> {
|
is KClass<*> -> {
|
||||||
if (propertyTypeClassifier.isValue) {
|
if (propertyTypeClassifier.isValue) {
|
||||||
@@ -298,8 +297,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
|
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
|
||||||
name,
|
jsonName,
|
||||||
jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: name,
|
|
||||||
adapter,
|
adapter,
|
||||||
property as KProperty1<Any, Any?>,
|
property as KProperty1<Any, Any?>,
|
||||||
parameter,
|
parameter,
|
||||||
@@ -323,7 +321,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val nonIgnoredBindings = bindings.filterNotNull()
|
val nonIgnoredBindings = bindings.filterNotNull()
|
||||||
val options = JsonReader.Options.of(*nonIgnoredBindings.map { it.name }.toTypedArray())
|
val options = JsonReader.Options.of(*nonIgnoredBindings.map { it.jsonName }.toTypedArray())
|
||||||
return KotlinJsonAdapter(constructor, bindings, nonIgnoredBindings, options).nullSafe()
|
return KotlinJsonAdapter(constructor, bindings, nonIgnoredBindings, options).nullSafe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi;
|
package com.squareup.moshi;
|
||||||
|
|
||||||
|
import static com.squareup.moshi.internal.Util.jsonName;
|
||||||
import static com.squareup.moshi.internal.Util.resolve;
|
import static com.squareup.moshi.internal.Util.resolve;
|
||||||
|
|
||||||
import com.squareup.moshi.internal.Util;
|
import com.squareup.moshi.internal.Util;
|
||||||
@@ -147,12 +148,9 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
// Store it using the field's name. If there was already a field with this name, fail!
|
// Store it using the field's name. If there was already a field with this name, fail!
|
||||||
String name =
|
String jsonName = jsonName(fieldName, jsonAnnotation);
|
||||||
jsonAnnotation != null && !Json.UNSET_NAME.equals(jsonAnnotation.name())
|
FieldBinding<Object> fieldBinding = new FieldBinding<>(jsonName, field, adapter);
|
||||||
? jsonAnnotation.name()
|
FieldBinding<?> replaced = fieldBindings.put(jsonName, fieldBinding);
|
||||||
: fieldName;
|
|
||||||
FieldBinding<Object> fieldBinding = new FieldBinding<>(name, field, adapter);
|
|
||||||
FieldBinding<?> replaced = fieldBindings.put(name, fieldBinding);
|
|
||||||
if (replaced != null) {
|
if (replaced != null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Conflicting fields:\n"
|
"Conflicting fields:\n"
|
||||||
|
@@ -274,12 +274,8 @@ final class StandardJsonAdapters {
|
|||||||
nameStrings = new String[constants.length];
|
nameStrings = new String[constants.length];
|
||||||
for (int i = 0; i < constants.length; i++) {
|
for (int i = 0; i < constants.length; i++) {
|
||||||
T constant = constants[i];
|
T constant = constants[i];
|
||||||
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
|
String constantName = constant.name();
|
||||||
String name =
|
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
|
||||||
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
|
|
||||||
? annotation.name()
|
|
||||||
: constant.name();
|
|
||||||
nameStrings[i] = name;
|
|
||||||
}
|
}
|
||||||
options = JsonReader.Options.of(nameStrings);
|
options = JsonReader.Options.of(nameStrings);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
|
@@ -19,6 +19,7 @@ import static com.squareup.moshi.Types.arrayOf;
|
|||||||
import static com.squareup.moshi.Types.subtypeOf;
|
import static com.squareup.moshi.Types.subtypeOf;
|
||||||
import static com.squareup.moshi.Types.supertypeOf;
|
import static com.squareup.moshi.Types.supertypeOf;
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json;
|
||||||
import com.squareup.moshi.JsonAdapter;
|
import com.squareup.moshi.JsonAdapter;
|
||||||
import com.squareup.moshi.JsonClass;
|
import com.squareup.moshi.JsonClass;
|
||||||
import com.squareup.moshi.JsonDataException;
|
import com.squareup.moshi.JsonDataException;
|
||||||
@@ -95,6 +96,16 @@ public final class Util {
|
|||||||
|
|
||||||
private Util() {}
|
private Util() {}
|
||||||
|
|
||||||
|
public static String jsonName(String declaredName, AnnotatedElement element) {
|
||||||
|
return jsonName(declaredName, element.getAnnotation(Json.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String jsonName(String declaredName, @Nullable Json annotation) {
|
||||||
|
if (annotation == null) return declaredName;
|
||||||
|
String annotationName = annotation.name();
|
||||||
|
return Json.UNSET_NAME.equals(annotationName) ? declaredName : annotationName;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean typesMatch(Type pattern, Type candidate) {
|
public static boolean typesMatch(Type pattern, Type candidate) {
|
||||||
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
|
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
|
||||||
return Types.equals(pattern, candidate);
|
return Types.equals(pattern, candidate);
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi;
|
package com.squareup.moshi;
|
||||||
|
|
||||||
|
import static com.squareup.moshi.internal.Util.rethrowCause;
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
|
|
||||||
import com.squareup.moshi.internal.Util;
|
import com.squareup.moshi.internal.Util;
|
||||||
@@ -25,9 +26,8 @@ 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.util.Collections;
|
import java.lang.reflect.Type;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -39,80 +39,81 @@ import java.util.Set;
|
|||||||
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
||||||
|
|
||||||
static final JsonAdapter.Factory FACTORY =
|
static final JsonAdapter.Factory FACTORY =
|
||||||
(type, annotations, moshi) -> {
|
new Factory() {
|
||||||
if (!annotations.isEmpty()) {
|
@Override
|
||||||
return null;
|
public JsonAdapter<?> create(
|
||||||
}
|
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
|
||||||
|
if (!annotations.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
|
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawType = Types.getRawType(type);
|
var rawType = Types.getRawType(type);
|
||||||
if (!rawType.isRecord()) {
|
if (!rawType.isRecord()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var components = rawType.getRecordComponents();
|
var components = rawType.getRecordComponents();
|
||||||
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
||||||
var componentRawTypes = 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];
|
||||||
componentRawTypes[i] = component.getType();
|
componentRawTypes[i] = component.getType();
|
||||||
var name = component.getName();
|
ComponentBinding<Object> componentBinding =
|
||||||
var componentType = Util.resolve(type, rawType, component.getGenericType());
|
createComponentBinding(type, rawType, moshi, lookup, component);
|
||||||
var jsonName = name;
|
var replaced = bindings.put(componentBinding.jsonName, componentBinding);
|
||||||
Set<Annotation> qualifiers = null;
|
if (replaced != null) {
|
||||||
for (var annotation : component.getDeclaredAnnotations()) {
|
throw new IllegalArgumentException(
|
||||||
if (annotation instanceof Json jsonAnnotation) {
|
"Conflicting components:\n"
|
||||||
var annotationName = jsonAnnotation.name();
|
+ " "
|
||||||
if (!Json.UNSET_NAME.equals(annotationName)) {
|
+ replaced.componentName
|
||||||
jsonName = jsonAnnotation.name();
|
+ "\n"
|
||||||
}
|
+ " "
|
||||||
} else {
|
+ componentBinding.componentName);
|
||||||
if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
|
|
||||||
if (qualifiers == null) {
|
|
||||||
qualifiers = new LinkedHashSet<>();
|
|
||||||
}
|
|
||||||
qualifiers.add(annotation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (qualifiers == null) {
|
|
||||||
qualifiers = Collections.emptySet();
|
MethodHandle constructor;
|
||||||
|
try {
|
||||||
|
constructor =
|
||||||
|
lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComponentBinding<Object> createComponentBinding(
|
||||||
|
Type type,
|
||||||
|
Class<?> rawType,
|
||||||
|
Moshi moshi,
|
||||||
|
MethodHandles.Lookup lookup,
|
||||||
|
RecordComponent component) {
|
||||||
|
var componentName = component.getName();
|
||||||
|
var jsonName = Util.jsonName(componentName, component);
|
||||||
|
|
||||||
|
var componentType = Util.resolve(type, rawType, component.getGenericType());
|
||||||
|
Set<? extends Annotation> qualifiers = Util.jsonAnnotations(component);
|
||||||
var adapter = moshi.adapter(componentType, qualifiers);
|
var adapter = moshi.adapter(componentType, qualifiers);
|
||||||
|
|
||||||
MethodHandle accessor;
|
MethodHandle accessor;
|
||||||
try {
|
try {
|
||||||
accessor = lookup.unreflect(component.getAccessor());
|
accessor = lookup.unreflect(component.getAccessor());
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new AssertionError(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;
|
return new ComponentBinding<>(componentName, jsonName, adapter, accessor);
|
||||||
try {
|
|
||||||
constructor = lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
}
|
||||||
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static record ComponentBinding<T>(
|
private static record ComponentBinding<T>(
|
||||||
String name, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {}
|
String componentName, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {}
|
||||||
|
|
||||||
private final String targetClass;
|
private final String targetClass;
|
||||||
private final MethodHandle constructor;
|
private final MethodHandle constructor;
|
||||||
@@ -146,23 +147,17 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var result = componentBindingsArray[index].adapter.fromJson(reader);
|
resultsArray[index] = componentBindingsArray[index].adapter.fromJson(reader);
|
||||||
resultsArray[index] = result;
|
|
||||||
}
|
}
|
||||||
reader.endObject();
|
reader.endObject();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (T) constructor.invokeWithArguments(resultsArray);
|
return (T) constructor.invokeWithArguments(resultsArray);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw rethrowCause(e);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if (e instanceof InvocationTargetException ite) {
|
throw new AssertionError(e);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,15 +170,10 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
Object componentValue;
|
Object componentValue;
|
||||||
try {
|
try {
|
||||||
componentValue = binding.accessor.invoke(value);
|
componentValue = binding.accessor.invoke(value);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw Util.rethrowCause(e);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if (e instanceof InvocationTargetException ite) {
|
throw new AssertionError(e);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
binding.adapter.toJson(writer, componentValue);
|
binding.adapter.toJson(writer, componentValue);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user