mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Allow custom generators (#847)
* Extract generatedJsonAdapterName to public API for other generators/consumers * Fix kapt location in tests * Add IDE-generated dependency-reduced-pom.xml to gitignore This always bites me * Add generator property to JsonClass and skip in processor * Opportunistically fix formatting for generateAdapter doc * Extract NullSafeJsonAdapter for delegate testing * Add custom adapter tests * Allow no-moshi constructors for generated adapters * Fix rebase issue * Use something other than nullSafe() for lenient check This no longer propagates lenient * Add missing copyrights * Add top-level class note * Add note about working against Moshi's generated signature * Add missing bit to "requirements for" * Note kotlin requirement relaxed in custom generators * Style
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ lib
|
|||||||
target
|
target
|
||||||
pom.xml.*
|
pom.xml.*
|
||||||
release.properties
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
|
@@ -85,7 +85,7 @@ class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
|||||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||||
for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
|
for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||||
val jsonClass = type.getAnnotation(annotation)
|
val jsonClass = type.getAnnotation(annotation)
|
||||||
if (jsonClass.generateAdapter) {
|
if (jsonClass.generateAdapter && jsonClass.generator.isEmpty()) {
|
||||||
val generator = adapterGenerator(type) ?: continue
|
val generator = adapterGenerator(type) ?: continue
|
||||||
generator.generateFile(generatedType)
|
generator.generateFile(generatedType)
|
||||||
.writeTo(filer)
|
.writeTo(filer)
|
||||||
|
@@ -26,6 +26,7 @@ import com.squareup.moshi.JsonWriter
|
|||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.squareup.moshi.ToJson
|
import com.squareup.moshi.ToJson
|
||||||
import com.squareup.moshi.Types
|
import com.squareup.moshi.Types
|
||||||
|
import com.squareup.moshi.internal.NullSafeJsonAdapter
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
@@ -1155,37 +1156,70 @@ class GeneratedAdaptersTest {
|
|||||||
assertThat(decoded).isEqualTo(HasCollectionOfPrimitives(listOf(4, -5, 6)))
|
assertThat(decoded).isEqualTo(HasCollectionOfPrimitives(listOf(4, -5, 6)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@JsonClass(generateAdapter = true, generator = "custom")
|
||||||
* This is here mostly just to ensure it still compiles. Covers variance, @Json, default values,
|
data class CustomGeneratedClass(val foo: String)
|
||||||
* nullability, primitive arrays, and some wacky generics.
|
|
||||||
*/
|
@Test fun customGenerator_withClassPresent() {
|
||||||
@JsonClass(generateAdapter = true)
|
val moshi = Moshi.Builder().build()
|
||||||
data class SmokeTestType(
|
val adapter = moshi.adapter(CustomGeneratedClass::class.java)
|
||||||
@Json(name = "first_name") val firstName: String,
|
val unwrapped = (adapter as NullSafeJsonAdapter<CustomGeneratedClass>).delegate()
|
||||||
@Json(name = "last_name") val lastName: String,
|
assertThat(unwrapped).isInstanceOf(GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter::class.java)
|
||||||
val age: Int,
|
}
|
||||||
val nationalities: List<String> = emptyList(),
|
|
||||||
val weight: Float,
|
@JsonClass(generateAdapter = true, generator = "custom")
|
||||||
val tattoos: Boolean = false,
|
data class CustomGeneratedClassMissing(val foo: String)
|
||||||
val race: String?,
|
|
||||||
val hasChildren: Boolean = false,
|
@Test fun customGenerator_withClassMissing() {
|
||||||
val favoriteFood: String? = null,
|
val moshi = Moshi.Builder().build()
|
||||||
val favoriteDrink: String? = "Water",
|
try {
|
||||||
val wildcardOut: MutableList<out String> = mutableListOf(),
|
moshi.adapter(CustomGeneratedClassMissing::class.java)
|
||||||
val nullableWildcardOut: MutableList<out String?> = mutableListOf(),
|
fail()
|
||||||
val wildcardIn: Array<in String>,
|
} catch (e: RuntimeException) {
|
||||||
val any: List<*>,
|
assertThat(e).hasMessageContaining("Failed to find the generated JsonAdapter class")
|
||||||
val anyTwo: List<Any>,
|
}
|
||||||
val anyOut: MutableList<out Any>,
|
}
|
||||||
val nullableAnyOut: MutableList<out Any?>,
|
|
||||||
val favoriteThreeNumbers: IntArray,
|
|
||||||
val favoriteArrayValues: Array<String>,
|
|
||||||
val favoriteNullableArrayValues: Array<String?>,
|
|
||||||
val nullableSetListMapArrayNullableIntWithDefault: Set<List<Map<String, Array<IntArray?>>>>? = null,
|
|
||||||
val aliasedName: TypeAliasName = "Woah",
|
|
||||||
val genericAlias: GenericTypeAlias = listOf("Woah")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has to be outside to avoid Types seeing an owning class
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class NullableTypeParams<T>(
|
||||||
|
val nullableList: List<String?>,
|
||||||
|
val nullableSet: Set<String?>,
|
||||||
|
val nullableMap: Map<String, String?>,
|
||||||
|
val nullableT: T?,
|
||||||
|
val nonNullT: T
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is here mostly just to ensure it still compiles. Covers variance, @Json, default values,
|
||||||
|
* nullability, primitive arrays, and some wacky generics.
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class SmokeTestType(
|
||||||
|
@Json(name = "first_name") val firstName: String,
|
||||||
|
@Json(name = "last_name") val lastName: String,
|
||||||
|
val age: Int,
|
||||||
|
val nationalities: List<String> = emptyList(),
|
||||||
|
val weight: Float,
|
||||||
|
val tattoos: Boolean = false,
|
||||||
|
val race: String?,
|
||||||
|
val hasChildren: Boolean = false,
|
||||||
|
val favoriteFood: String? = null,
|
||||||
|
val favoriteDrink: String? = "Water",
|
||||||
|
val wildcardOut: MutableList<out String> = mutableListOf(),
|
||||||
|
val nullableWildcardOut: MutableList<out String?> = mutableListOf(),
|
||||||
|
val wildcardIn: Array<in String>,
|
||||||
|
val any: List<*>,
|
||||||
|
val anyTwo: List<Any>,
|
||||||
|
val anyOut: MutableList<out Any>,
|
||||||
|
val nullableAnyOut: MutableList<out Any?>,
|
||||||
|
val favoriteThreeNumbers: IntArray,
|
||||||
|
val favoriteArrayValues: Array<String>,
|
||||||
|
val favoriteNullableArrayValues: Array<String?>,
|
||||||
|
val nullableSetListMapArrayNullableIntWithDefault: Set<List<Map<String, Array<IntArray?>>>>? = null,
|
||||||
|
val aliasedName: TypeAliasName = "Woah",
|
||||||
|
val genericAlias: GenericTypeAlias = listOf("Woah")
|
||||||
|
)
|
||||||
|
|
||||||
typealias TypeAliasName = String
|
typealias TypeAliasName = String
|
||||||
typealias GenericTypeAlias = List<String>
|
typealias GenericTypeAlias = List<String>
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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
|
||||||
|
*
|
||||||
|
* http://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.kotlin.codgen
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.JsonReader
|
||||||
|
import com.squareup.moshi.JsonWriter
|
||||||
|
import com.squareup.moshi.kotlin.codgen.GeneratedAdaptersTest.CustomGeneratedClass
|
||||||
|
|
||||||
|
// This also tests custom generated types with no moshi constructor
|
||||||
|
class GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter : JsonAdapter<CustomGeneratedClass>() {
|
||||||
|
override fun fromJson(reader: JsonReader): CustomGeneratedClass? {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: CustomGeneratedClass?) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi;
|
package com.squareup.moshi;
|
||||||
|
|
||||||
|
import com.squareup.moshi.internal.NullSafeJsonAdapter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
@@ -128,29 +129,7 @@ public abstract class JsonAdapter<T> {
|
|||||||
* nulls.
|
* nulls.
|
||||||
*/
|
*/
|
||||||
@CheckReturnValue public final JsonAdapter<T> nullSafe() {
|
@CheckReturnValue public final JsonAdapter<T> nullSafe() {
|
||||||
final JsonAdapter<T> delegate = this;
|
return new NullSafeJsonAdapter<>(this);
|
||||||
return new JsonAdapter<T>() {
|
|
||||||
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
|
|
||||||
if (reader.peek() == JsonReader.Token.NULL) {
|
|
||||||
return reader.nextNull();
|
|
||||||
} else {
|
|
||||||
return delegate.fromJson(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
} else {
|
|
||||||
delegate.toJson(writer, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override boolean isLenient() {
|
|
||||||
return delegate.isLenient();
|
|
||||||
}
|
|
||||||
@Override public String toString() {
|
|
||||||
return delegate + ".nullSafe()";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,6 +17,7 @@ package com.squareup.moshi;
|
|||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
@@ -29,13 +30,52 @@ public @interface JsonClass {
|
|||||||
/**
|
/**
|
||||||
* True to trigger the annotation processor to generate an adapter for this type.
|
* True to trigger the annotation processor to generate an adapter for this type.
|
||||||
*
|
*
|
||||||
* There are currently some restrictions on which types that can be used with generated adapters:
|
* <p>There are currently some restrictions on which types that can be used with generated
|
||||||
*
|
* adapters:
|
||||||
* * The class must be implemented in Kotlin.
|
* <ul>
|
||||||
* * The class may not be an abstract class, an inner class, or a local class.
|
* <li>
|
||||||
* * All superclasses must be implemented in Kotlin.
|
* The class must be implemented in Kotlin (unless using a custom generator, see
|
||||||
* * All properties must be public, protected, or internal.
|
* {@link #generator()}).
|
||||||
* * All properties must be either non-transient or have a default value.
|
* </li>
|
||||||
|
* <li>The class may not be an abstract class, an inner class, or a local class.</li>
|
||||||
|
* <li>All superclasses must be implemented in Kotlin.</li>
|
||||||
|
* <li>All properties must be public, protected, or internal.</li>
|
||||||
|
* <li>All properties must be either non-transient or have a default value.</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
boolean generateAdapter();
|
boolean generateAdapter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional custom generator tag used to indicate which generator should be used. If empty,
|
||||||
|
* Moshi's annotation processor will generate an adapter for the annotated type. If not empty,
|
||||||
|
* Moshi's processor will skip it and defer to a custom generator. This can be used to allow
|
||||||
|
* other custom code generation tools to run and still allow Moshi to read their generated
|
||||||
|
* JsonAdapter outputs.
|
||||||
|
*
|
||||||
|
* <p>Requirements for generated adapter class signatures:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* The generated adapter must subclass {@link JsonAdapter} and be parameterized by this type.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* {@link Types#generatedJsonAdapterName} should be used for the fully qualified class name in
|
||||||
|
* order for Moshi to correctly resolve and load the generated JsonAdapter.
|
||||||
|
* </li>
|
||||||
|
* <li>The first parameter must be a {@link Moshi} instance.</li>
|
||||||
|
* <li>
|
||||||
|
* If generic, a second {@link Type[]} parameter should be declared to accept type arguments.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Example for a class "CustomType":<pre>{@code
|
||||||
|
* class CustomTypeJsonAdapter(moshi: Moshi, types: Array<Type>) : JsonAdapter<CustomType>() {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>To help ensure your own generator meets requirements above, you can use Moshi’s built-in
|
||||||
|
* generator to create the API signature to get started, then make your own generator match that
|
||||||
|
* expected signature.
|
||||||
|
*/
|
||||||
|
String generator() default "";
|
||||||
}
|
}
|
||||||
|
@@ -49,6 +49,37 @@ public final class Types {
|
|||||||
private Types() {
|
private Types() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given
|
||||||
|
* {@link JsonClass JsonClass-annotated} {@code clazz}. This is the same lookup logic used by
|
||||||
|
* both the Moshi code generation as well as lookup for any JsonClass-annotated classes. This can
|
||||||
|
* be useful if generating your own JsonAdapters without using Moshi's first party code gen.
|
||||||
|
*
|
||||||
|
* @param clazz the class to calculate a generated JsonAdapter name for.
|
||||||
|
* @return the resolved fully qualified class name to the expected generated JsonAdapter class.
|
||||||
|
* Note that this name will always be a top-level class name and not a nested class.
|
||||||
|
*/
|
||||||
|
public static String generatedJsonAdapterName(Class<?> clazz) {
|
||||||
|
if (clazz.getAnnotation(JsonClass.class) == null) {
|
||||||
|
throw new IllegalArgumentException("Class does not have a JsonClass annotation: " + clazz);
|
||||||
|
}
|
||||||
|
return generatedJsonAdapterName(clazz.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given
|
||||||
|
* {@link JsonClass JsonClass-annotated} {@code className}. This is the same lookup logic used by
|
||||||
|
* both the Moshi code generation as well as lookup for any JsonClass-annotated classes. This can
|
||||||
|
* be useful if generating your own JsonAdapters without using Moshi's first party code gen.
|
||||||
|
*
|
||||||
|
* @param className the fully qualified class to calculate a generated JsonAdapter name for.
|
||||||
|
* @return the resolved fully qualified class name to the expected generated JsonAdapter class.
|
||||||
|
* Note that this name will always be a top-level class name and not a nested class.
|
||||||
|
*/
|
||||||
|
public static String generatedJsonAdapterName(String className) {
|
||||||
|
return className.replace("$", "_") + "JsonAdapter";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if {@code annotations} contains {@code jsonQualifier}.
|
* Checks if {@code annotations} contains {@code jsonQualifier}.
|
||||||
* Returns the subset of {@code annotations} without {@code jsonQualifier}, or null if {@code
|
* Returns the subset of {@code annotations} without {@code jsonQualifier}, or null if {@code
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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
|
||||||
|
*
|
||||||
|
* http://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.internal;
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonAdapter;
|
||||||
|
import com.squareup.moshi.JsonReader;
|
||||||
|
import com.squareup.moshi.JsonWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public final class NullSafeJsonAdapter<T> extends JsonAdapter<T> {
|
||||||
|
|
||||||
|
private final JsonAdapter<T> delegate;
|
||||||
|
|
||||||
|
public NullSafeJsonAdapter(JsonAdapter<T> delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonAdapter<T> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
|
||||||
|
if (reader.peek() == JsonReader.Token.NULL) {
|
||||||
|
return reader.nextNull();
|
||||||
|
} else {
|
||||||
|
return delegate.fromJson(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
writer.nullValue();
|
||||||
|
} else {
|
||||||
|
delegate.toJson(writer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String toString() {
|
||||||
|
return delegate + ".nullSafe()";
|
||||||
|
}
|
||||||
|
}
|
@@ -460,23 +460,35 @@ public final class Util {
|
|||||||
if (jsonClass == null || !jsonClass.generateAdapter()) {
|
if (jsonClass == null || !jsonClass.generateAdapter()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String adapterClassName = rawType.getName().replace("$", "_") + "JsonAdapter";
|
String adapterClassName = Types.generatedJsonAdapterName(rawType.getName());
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings("unchecked") // We generate types to match.
|
@SuppressWarnings("unchecked") // We generate types to match.
|
||||||
Class<? extends JsonAdapter<?>> adapterClass = (Class<? extends JsonAdapter<?>>)
|
Class<? extends JsonAdapter<?>> adapterClass = (Class<? extends JsonAdapter<?>>)
|
||||||
Class.forName(adapterClassName, true, rawType.getClassLoader());
|
Class.forName(adapterClassName, true, rawType.getClassLoader());
|
||||||
|
Constructor<? extends JsonAdapter<?>> constructor;
|
||||||
|
Object[] args;
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
Constructor<? extends JsonAdapter<?>> constructor
|
Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
|
||||||
= adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
|
try {
|
||||||
constructor.setAccessible(true);
|
// Common case first
|
||||||
return constructor.newInstance(moshi, ((ParameterizedType) type).getActualTypeArguments())
|
constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
|
||||||
.nullSafe();
|
args = new Object[] { moshi, typeArgs };
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
constructor = adapterClass.getDeclaredConstructor(Type[].class);
|
||||||
|
args = new Object[] { typeArgs };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Constructor<? extends JsonAdapter<?>> constructor
|
try {
|
||||||
= adapterClass.getDeclaredConstructor(Moshi.class);
|
// Common case first
|
||||||
constructor.setAccessible(true);
|
constructor = adapterClass.getDeclaredConstructor(Moshi.class);
|
||||||
return constructor.newInstance(moshi).nullSafe();
|
args = new Object[] { moshi };
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
constructor = adapterClass.getDeclaredConstructor();
|
||||||
|
args = new Object[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
return constructor.newInstance(args).nullSafe();
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Failed to find the generated JsonAdapter class for " + rawType, e);
|
"Failed to find the generated JsonAdapter class for " + rawType, e);
|
||||||
|
@@ -282,7 +282,7 @@ public final class JsonAdapterTest {
|
|||||||
@Override public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
|
@Override public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}.lenient().nullSafe();
|
}.lenient().nonNull();
|
||||||
assertThat(adapter.fromJson("true true")).isEqualTo(true);
|
assertThat(adapter.fromJson("true true")).isEqualTo(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -287,6 +287,33 @@ public final class TypesTest {
|
|||||||
assertThat(annotations).hasSize(0);
|
assertThat(annotations).hasSize(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void generatedJsonAdapterName_strings() {
|
||||||
|
assertThat(Types.generatedJsonAdapterName("com.foo.Test")).isEqualTo("com.foo.TestJsonAdapter");
|
||||||
|
assertThat(Types.generatedJsonAdapterName("com.foo.Test$Bar")).isEqualTo("com.foo.Test_BarJsonAdapter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void generatedJsonAdapterName_class() {
|
||||||
|
assertThat(Types.generatedJsonAdapterName(TestJsonClass.class)).isEqualTo("com.squareup.moshi.TypesTest_TestJsonClassJsonAdapter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void generatedJsonAdapterName_class_missingJsonClass() {
|
||||||
|
try {
|
||||||
|
Types.generatedJsonAdapterName(TestNonJsonClass.class);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertThat(e).hasMessageContaining("Class does not have a JsonClass annotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = false)
|
||||||
|
static class TestJsonClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestNonJsonClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@JsonQualifier
|
@JsonQualifier
|
||||||
@Target(FIELD)
|
@Target(FIELD)
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
|
Reference in New Issue
Block a user