mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Report json name if different from property name in kotlin (#917)
* Report json name in code gen if different from property name Resolves #800 * Unify required property name error message * Report json name in kotlinjsonadapter, match code gen * Upper case JSON Co-Authored-By: Jake Wharton <jakew@google.com> * Upper case JSON Co-Authored-By: Jake Wharton <jakew@google.com> * Don't keep constants * Inline json name - property name comparison to util methods * Remove unnecessary constructor keyword * Consolidate non-null/missing property tests to parameterized suite * Add custom json name tests for nonNull property checks * Rename test to make maven happy Maven won't run the test unless it ends with `Test` or `TestCase`
This commit is contained in:
@@ -203,7 +203,7 @@ internal class AdapterGenerator(
|
||||
result.addStatement("%N = %N.fromJson(%N)",
|
||||
property.localName, nameAllocator[property.delegateKey], readerParam)
|
||||
} else {
|
||||
val exception = unexpectedNull(property.localName, readerParam)
|
||||
val exception = unexpectedNull(property, readerParam)
|
||||
result.addStatement("%N = %N.fromJson(%N) ?: throw·%L",
|
||||
property.localName, nameAllocator[property.delegateKey], readerParam, exception)
|
||||
}
|
||||
@@ -221,7 +221,7 @@ internal class AdapterGenerator(
|
||||
result.addStatement("%L -> %N = %N.fromJson(%N)",
|
||||
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam)
|
||||
} else {
|
||||
val exception = unexpectedNull(property.localName, readerParam)
|
||||
val exception = unexpectedNull(property, readerParam)
|
||||
result.addStatement("%L -> %N = %N.fromJson(%N) ?: throw·%L",
|
||||
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam,
|
||||
exception)
|
||||
@@ -308,7 +308,8 @@ internal class AdapterGenerator(
|
||||
}
|
||||
if (!property.isTransient && property.isRequired) {
|
||||
val missingPropertyBlock =
|
||||
CodeBlock.of("%T.missingProperty(%S, %N)", Util::class, property.localName, readerParam)
|
||||
CodeBlock.of("%T.missingProperty(%S, %S, %N)",
|
||||
MOSHI_UTIL, property.localName, property.jsonName, readerParam)
|
||||
result.addCode(" ?: throw·%L", missingPropertyBlock)
|
||||
}
|
||||
separator = ",\n"
|
||||
@@ -341,8 +342,9 @@ internal class AdapterGenerator(
|
||||
return result.build()
|
||||
}
|
||||
|
||||
private fun unexpectedNull(identifier: String, reader: ParameterSpec): CodeBlock {
|
||||
return CodeBlock.of("%T.unexpectedNull(%S, %N)", Util::class, identifier, reader)
|
||||
private fun unexpectedNull(property: PropertyGenerator, reader: ParameterSpec): CodeBlock {
|
||||
return CodeBlock.of("%T.unexpectedNull(%S, %S, %N)",
|
||||
MOSHI_UTIL, property.localName, property.jsonName, reader)
|
||||
}
|
||||
|
||||
private fun generateToJsonFun(): FunSpec {
|
||||
|
@@ -83,8 +83,11 @@ internal class KotlinJsonAdapter<T>(
|
||||
values[index] = binding.adapter.fromJson(reader)
|
||||
|
||||
if (values[index] == null && !binding.property.returnType.isMarkedNullable) {
|
||||
throw JsonDataException(
|
||||
"Non-null value '${binding.property.name}' was null at ${reader.path}")
|
||||
throw Util.unexpectedNull(
|
||||
binding.property.name,
|
||||
binding.jsonName,
|
||||
reader
|
||||
)
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
@@ -93,8 +96,11 @@ internal class KotlinJsonAdapter<T>(
|
||||
for (i in 0 until constructorSize) {
|
||||
if (values[i] === ABSENT_VALUE && !constructor.parameters[i].isOptional) {
|
||||
if (!constructor.parameters[i].type.isMarkedNullable) {
|
||||
throw JsonDataException(
|
||||
"Required value '${constructor.parameters[i].name}' missing at ${reader.path}")
|
||||
throw Util.missingProperty(
|
||||
constructor.parameters[i].name,
|
||||
bindings[i]?.jsonName,
|
||||
reader
|
||||
)
|
||||
}
|
||||
values[i] = null // Replace absent with null.
|
||||
}
|
||||
@@ -130,6 +136,7 @@ internal class KotlinJsonAdapter<T>(
|
||||
|
||||
data class Binding<K, P>(
|
||||
val name: String,
|
||||
val jsonName: String?,
|
||||
val adapter: JsonAdapter<P>,
|
||||
val property: KProperty1<K, P>,
|
||||
val parameter: KParameter?) {
|
||||
@@ -245,8 +252,13 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
||||
resolvedPropertyType, Util.jsonAnnotations(allAnnotations.toTypedArray()), property.name)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
bindingsByName[property.name] = KotlinJsonAdapter.Binding(name, adapter,
|
||||
property as KProperty1<Any, Any?>, parameter)
|
||||
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
|
||||
name,
|
||||
jsonAnnotation?.name ?: name,
|
||||
adapter,
|
||||
property as KProperty1<Any, Any?>,
|
||||
parameter
|
||||
)
|
||||
}
|
||||
|
||||
val bindings = ArrayList<KotlinJsonAdapter.Binding<Any, Any?>?>()
|
||||
|
@@ -0,0 +1,255 @@
|
||||
package com.squareup.moshi.kotlin
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonAdapter.Factory
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.ToJson
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.squareup.moshi.kotlin.reflect.adapter
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.junit.runners.Parameterized.Parameters
|
||||
import java.lang.reflect.Type
|
||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
|
||||
/**
|
||||
* Parameterized tests that test serialization with both [KotlinJsonAdapterFactory] and code gen.
|
||||
*/
|
||||
@RunWith(Parameterized::class)
|
||||
class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
companion object {
|
||||
@Parameters(name = "reflective={0}")
|
||||
@JvmStatic
|
||||
fun parameters(): List<Array<*>> {
|
||||
return listOf(
|
||||
arrayOf(true),
|
||||
arrayOf(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val moshi = Moshi.Builder()
|
||||
.apply {
|
||||
if (useReflection) {
|
||||
add(KotlinJsonAdapterFactory())
|
||||
add(object : Factory {
|
||||
override fun create(
|
||||
type: Type,
|
||||
annotations: MutableSet<out Annotation>,
|
||||
moshi: Moshi
|
||||
): JsonAdapter<*>? {
|
||||
// Prevent falling back to generated adapter lookup
|
||||
val rawType = Types.getRawType(type)
|
||||
val metadataClass = Class.forName("kotlin.Metadata") as Class<out Annotation>
|
||||
check(!rawType.isAnnotationPresent(metadataClass)) {
|
||||
"Unhandled Kotlin type in reflective test! $rawType"
|
||||
}
|
||||
return moshi.nextAdapter<Any>(this, type, annotations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
|
||||
@Test fun requiredValueAbsent() {
|
||||
val jsonAdapter = moshi.adapter<RequiredValueAbsent>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Required value 'b' missing at $")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredValueAbsent(var a: Int = 3, var b: Int)
|
||||
|
||||
@Test fun requiredValueWithDifferentJsonNameAbsent() {
|
||||
val jsonAdapter = moshi.adapter<RequiredValueWithDifferentJsonNameAbsent>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Required value 'b' (JSON name 'bPrime') missing at \$")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredValueWithDifferentJsonNameAbsent(var a: Int = 3, @Json(name = "bPrime") var b: Int)
|
||||
|
||||
@Test fun nonNullPropertySetToNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertySetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class HasNonNullProperty {
|
||||
var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertyWithJsonNameSetToNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"aPrime\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' (JSON name 'aPrime') was null at \$.aPrime")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertyWithJsonNameSetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"aPrime\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' (JSON name 'aPrime') was null at \$.aPrime")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class HasNonNullPropertyDifferentJsonName {
|
||||
@Json(name = "aPrime") var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RUNTIME)
|
||||
annotation class Nullable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNonNullConstructorParameter(val a: String)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableConstructorParameter(val a: String?)
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullChecking() {
|
||||
val localMoshi = moshi.newBuilder()
|
||||
.add(object {
|
||||
@FromJson
|
||||
fun fromJson(@Nullable string: String?): String {
|
||||
return string ?: "fallback"
|
||||
}
|
||||
|
||||
@ToJson
|
||||
fun toJson(@Nullable value: String?): String {
|
||||
return value ?: "fallback"
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
val hasNonNullConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNonNullConstructorParameter>()
|
||||
assertThat(hasNonNullConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNonNullConstructorParameter("fallback"))
|
||||
|
||||
val hasNullableConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNullableConstructorParameter>()
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNullableConstructorParameter("fallback"))
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.toJson(HasNullableConstructorParameter(null))).isEqualTo("{\"a\":\"fallback\"}")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableTypeWithGeneratedAdapter(val a: HasNonNullConstructorParameter?)
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullCheckingWithGeneratedAdapter() {
|
||||
val adapter = moshi.adapter<HasNullableTypeWithGeneratedAdapter>()
|
||||
|
||||
val encoded = HasNullableTypeWithGeneratedAdapter(null)
|
||||
//language=JSON
|
||||
assertThat(adapter.toJson(encoded)).isEqualTo("""{}""")
|
||||
//language=JSON
|
||||
assertThat(adapter.serializeNulls().toJson(encoded)).isEqualTo("""{"a":null}""")
|
||||
|
||||
//language=JSON
|
||||
val decoded = adapter.fromJson("""{"a":null}""")!!
|
||||
assertThat(decoded.a).isEqualTo(null)
|
||||
}
|
||||
|
||||
}
|
@@ -410,53 +410,6 @@ class GeneratedAdaptersTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
|
||||
|
||||
@Test fun requiredValueAbsent() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<RequiredValueAbsent>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Required property 'b' missing at \$")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredValueAbsent(var a: Int = 3, var b: Int)
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFromAdapterFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(object {
|
||||
@FromJson fun fromJson(string: String): String? = null
|
||||
}).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNonNullConstructorParameter(val a: String)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableConstructorParameter(val a: String?)
|
||||
|
||||
@Test fun explicitNull() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<ExplicitNull>()
|
||||
@@ -610,37 +563,6 @@ class GeneratedAdaptersTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertySetToNullFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertySetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(object {
|
||||
@FromJson fun fromJson(string: String): String? = null
|
||||
}).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class HasNonNullProperty {
|
||||
var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun manyProperties32() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<ManyProperties32>()
|
||||
@@ -1114,62 +1036,11 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun adaptersAreNullSafe() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
val adapter = moshi.adapter<HasNullableBoolean>()
|
||||
assertThat(adapter.fromJson("null")).isNull()
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null")
|
||||
}
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Nullable
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullChecking() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(object {
|
||||
@FromJson fun fromJson(@Nullable string: String?): String {
|
||||
return string ?: "fallback"
|
||||
}
|
||||
|
||||
@ToJson fun toJson(@Nullable value: String?): String {
|
||||
return value ?: "fallback"
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
val hasNonNullConstructorParameterAdapter =
|
||||
moshi.adapter<HasNonNullConstructorParameter>()
|
||||
assertThat(hasNonNullConstructorParameterAdapter
|
||||
.fromJson("{\"a\":null}")).isEqualTo(
|
||||
HasNonNullConstructorParameter(
|
||||
"fallback"))
|
||||
|
||||
val hasNullableConstructorParameterAdapter =
|
||||
moshi.adapter<HasNullableConstructorParameter>()
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
.fromJson("{\"a\":null}")).isEqualTo(
|
||||
HasNullableConstructorParameter(
|
||||
"fallback"))
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
.toJson(
|
||||
HasNullableConstructorParameter(
|
||||
null))).isEqualTo("{\"a\":\"fallback\"}")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableTypeWithGeneratedAdapter(val a: HasNonNullConstructorParameter?)
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullCheckingWithGeneratedAdapter() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter = moshi.adapter<HasNullableTypeWithGeneratedAdapter>()
|
||||
|
||||
val encoded = HasNullableTypeWithGeneratedAdapter(
|
||||
null)
|
||||
assertThat(adapter.toJson(encoded)).isEqualTo("""{}""")
|
||||
assertThat(adapter.serializeNulls().toJson(encoded)).isEqualTo("""{"a":null}""")
|
||||
|
||||
val decoded = adapter.fromJson("""{"a":null}""")!!
|
||||
assertThat(decoded.a).isEqualTo(null)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasCollectionOfPrimitives(val listOfInts: List<Int>)
|
||||
|
||||
|
@@ -133,80 +133,6 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
|
||||
|
||||
@Test fun requiredValueAbsent() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<RequiredValueAbsent>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Required value 'b' missing at $")
|
||||
}
|
||||
}
|
||||
|
||||
class RequiredValueAbsent(var a: Int = 3, var b: Int)
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFromAdapterFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(object {
|
||||
@FromJson fun fromJson(string: String): String? = null
|
||||
}).add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
data class HasNonNullConstructorParameter(val a: String)
|
||||
|
||||
data class HasNullableConstructorParameter(val a: String?)
|
||||
|
||||
@Test fun nonNullPropertySetToNullFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertySetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val moshi = Moshi.Builder().add(object {
|
||||
@FromJson fun fromJson(string: String): String? = null
|
||||
}).add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
class HasNonNullProperty {
|
||||
var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun duplicatedValueParameter() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<DuplicateValueParameter>()
|
||||
@@ -875,32 +801,6 @@ class KotlinJsonAdapterTest {
|
||||
assertThat(adapter.toJson(value)).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Retention(RUNTIME)
|
||||
annotation class Nullable
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullChecking() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(object {
|
||||
@FromJson fun fromJson(@Nullable string: String?): String {
|
||||
return string ?: "fallback"
|
||||
}
|
||||
|
||||
@ToJson fun toJson(@Nullable value: String?): String {
|
||||
return value ?: "fallback"
|
||||
}
|
||||
})
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
assertThat(moshi.adapter<HasNonNullConstructorParameter>()
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNonNullConstructorParameter("fallback"))
|
||||
|
||||
assertThat(moshi.adapter<HasNullableConstructorParameter>()
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNullableConstructorParameter("fallback"))
|
||||
assertThat(moshi.adapter<HasNullableConstructorParameter>()
|
||||
.toJson(HasNullableConstructorParameter(null))).isEqualTo("{\"a\":\"fallback\"}")
|
||||
}
|
||||
|
||||
@Test fun mixingReflectionAndCodegen() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
@@ -970,7 +870,7 @@ class KotlinJsonAdapterTest {
|
||||
.build()
|
||||
|
||||
// TODO in CR: We had to mark this as nullable, vs before the jsonadapter factory would always run
|
||||
val adapter = moshi.adapter<HasNonNullConstructorParameter?>()
|
||||
val adapter = moshi.adapter<HasNullableBoolean?>()
|
||||
assertThat(adapter.fromJson("null")).isNull()
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null")
|
||||
}
|
||||
|
@@ -44,8 +44,6 @@ import static com.squareup.moshi.Types.subtypeOf;
|
||||
import static com.squareup.moshi.Types.supertypeOf;
|
||||
|
||||
public final class Util {
|
||||
private static final String REQUIRED_PROPERTY_TEMPLATE = "Required property '%s' missing at %s";
|
||||
private static final String UNEXPECTED_NULL_TEMPLATE = "Non-null value '%s' was null at %s";
|
||||
public static final Set<Annotation> NO_ANNOTATIONS = Collections.emptySet();
|
||||
public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
|
||||
@Nullable private static final Class<?> DEFAULT_CONSTRUCTOR_MARKER;
|
||||
@@ -567,17 +565,35 @@ public final class Util {
|
||||
throw new IllegalStateException("No defaults constructor found for " + targetClass);
|
||||
}
|
||||
|
||||
public static JsonDataException missingProperty(String property, JsonReader reader) {
|
||||
return jsonDataException(REQUIRED_PROPERTY_TEMPLATE, property, reader);
|
||||
public static JsonDataException missingProperty(
|
||||
String propertyName,
|
||||
String jsonName,
|
||||
JsonReader reader
|
||||
) {
|
||||
String path = reader.getPath();
|
||||
String message;
|
||||
if (jsonName.equals(propertyName)) {
|
||||
message = String.format("Required value '%s' missing at %s", propertyName, path);
|
||||
} else {
|
||||
message = String.format("Required value '%s' (JSON name '%s') missing at %s",
|
||||
propertyName, jsonName, path);
|
||||
}
|
||||
return new JsonDataException(message);
|
||||
}
|
||||
|
||||
public static JsonDataException unexpectedNull(String property, JsonReader reader) {
|
||||
return jsonDataException(UNEXPECTED_NULL_TEMPLATE, property, reader);
|
||||
}
|
||||
|
||||
private static JsonDataException jsonDataException(
|
||||
String template, String property, JsonReader reader) {
|
||||
return new JsonDataException(
|
||||
String.format(template, property, reader.getPath()));
|
||||
public static JsonDataException unexpectedNull(
|
||||
String propertyName,
|
||||
String jsonName,
|
||||
JsonReader reader
|
||||
) {
|
||||
String path = reader.getPath();
|
||||
String message;
|
||||
if (jsonName.equals(propertyName)) {
|
||||
message = String.format("Non-null value '%s' was null at %s", propertyName, path);
|
||||
} else {
|
||||
message = String.format("Non-null value '%s' (JSON name '%s') was null at %s",
|
||||
propertyName, jsonName, path);
|
||||
}
|
||||
return new JsonDataException(message);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user