mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 08:29:22 +08:00
Support multiple transient properties in KotlinJsonAdapter.
This commit is contained in:
committed by
Jesse Wilson
parent
5912dfaaf6
commit
efb0fc0923
@@ -54,35 +54,36 @@ private val ABSENT_VALUE = Any()
|
|||||||
* constructor, and then by setting any additional properties that exist, if any.
|
* constructor, and then by setting any additional properties that exist, if any.
|
||||||
*/
|
*/
|
||||||
internal class KotlinJsonAdapter<T>(
|
internal class KotlinJsonAdapter<T>(
|
||||||
private val constructor: KFunction<T>,
|
val constructor: KFunction<T>,
|
||||||
private val bindings: List<Binding<T, Any?>?>,
|
val allBindings: List<Binding<T, Any?>?>,
|
||||||
private val options: JsonReader.Options
|
val nonTransientBindings: List<Binding<T, Any?>>,
|
||||||
|
val options: JsonReader.Options
|
||||||
) : JsonAdapter<T>() {
|
) : JsonAdapter<T>() {
|
||||||
|
|
||||||
override fun fromJson(reader: JsonReader): T {
|
override fun fromJson(reader: JsonReader): T {
|
||||||
val constructorSize = constructor.parameters.size
|
val constructorSize = constructor.parameters.size
|
||||||
|
|
||||||
// Read each value into its slot in the array.
|
// Read each value into its slot in the array.
|
||||||
val values = Array<Any?>(bindings.size) { ABSENT_VALUE }
|
val values = Array<Any?>(allBindings.size) { ABSENT_VALUE }
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
while (reader.hasNext()) {
|
while (reader.hasNext()) {
|
||||||
val index = reader.selectName(options)
|
val index = reader.selectName(options)
|
||||||
val binding = if (index != -1) bindings[index] else null
|
if (index == -1) {
|
||||||
|
|
||||||
if (binding == null) {
|
|
||||||
reader.skipName()
|
reader.skipName()
|
||||||
reader.skipValue()
|
reader.skipValue()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
val binding = nonTransientBindings[index]
|
||||||
|
|
||||||
if (values[index] !== ABSENT_VALUE) {
|
val propertyIndex = binding.propertyIndex
|
||||||
|
if (values[propertyIndex] !== ABSENT_VALUE) {
|
||||||
throw JsonDataException(
|
throw JsonDataException(
|
||||||
"Multiple values for '${binding.property.name}' at ${reader.path}")
|
"Multiple values for '${binding.property.name}' at ${reader.path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
values[index] = binding.adapter.fromJson(reader)
|
values[propertyIndex] = binding.adapter.fromJson(reader)
|
||||||
|
|
||||||
if (values[index] == null && !binding.property.returnType.isMarkedNullable) {
|
if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
|
||||||
throw Util.unexpectedNull(
|
throw Util.unexpectedNull(
|
||||||
binding.property.name,
|
binding.property.name,
|
||||||
binding.jsonName,
|
binding.jsonName,
|
||||||
@@ -98,7 +99,7 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
if (!constructor.parameters[i].type.isMarkedNullable) {
|
if (!constructor.parameters[i].type.isMarkedNullable) {
|
||||||
throw Util.missingProperty(
|
throw Util.missingProperty(
|
||||||
constructor.parameters[i].name,
|
constructor.parameters[i].name,
|
||||||
bindings[i]?.jsonName,
|
allBindings[i]?.jsonName,
|
||||||
reader
|
reader
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -110,8 +111,8 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
val result = constructor.callBy(IndexedParameterMap(constructor.parameters, values))
|
val result = constructor.callBy(IndexedParameterMap(constructor.parameters, values))
|
||||||
|
|
||||||
// Set remaining properties.
|
// Set remaining properties.
|
||||||
for (i in constructorSize until bindings.size) {
|
for (i in constructorSize until allBindings.size) {
|
||||||
val binding = bindings[i]!!
|
val binding = allBindings[i]!!
|
||||||
val value = values[i]
|
val value = values[i]
|
||||||
binding.set(result, value)
|
binding.set(result, value)
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,7 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
if (value == null) throw NullPointerException("value == null")
|
if (value == null) throw NullPointerException("value == null")
|
||||||
|
|
||||||
writer.beginObject()
|
writer.beginObject()
|
||||||
for (binding in bindings) {
|
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.name)
|
||||||
@@ -139,7 +140,9 @@ internal class KotlinJsonAdapter<T>(
|
|||||||
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?,
|
||||||
|
val propertyIndex: Int
|
||||||
|
) {
|
||||||
fun get(value: K) = property.get(value)
|
fun get(value: K) = property.get(value)
|
||||||
|
|
||||||
fun set(result: K, value: P) {
|
fun set(result: K, value: P) {
|
||||||
@@ -257,7 +260,8 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
|||||||
jsonAnnotation?.name ?: name,
|
jsonAnnotation?.name ?: name,
|
||||||
adapter,
|
adapter,
|
||||||
property as KProperty1<Any, Any?>,
|
property as KProperty1<Any, Any?>,
|
||||||
parameter
|
parameter,
|
||||||
|
parameter?.index ?: -1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,9 +275,13 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
|||||||
bindings += binding
|
bindings += binding
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings += bindingsByName.values
|
var index = bindings.size
|
||||||
|
for (bindingByName in bindingsByName) {
|
||||||
|
bindings += bindingByName.value.copy(propertyIndex = index++)
|
||||||
|
}
|
||||||
|
|
||||||
val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray())
|
val nonTransientBindings = bindings.filterNotNull()
|
||||||
return KotlinJsonAdapter(constructor, bindings, options).nullSafe()
|
val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray())
|
||||||
|
return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -535,6 +535,22 @@ class GeneratedAdaptersTest {
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
|
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
|
||||||
|
|
||||||
|
@Test fun multipleTransientConstructorParameters() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(MultipleTransientConstructorParameters::class.java)
|
||||||
|
|
||||||
|
val encoded = MultipleTransientConstructorParameters(3, 5, 7)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(-1)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
assertThat(decoded.c).isEqualTo(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class MultipleTransientConstructorParameters(@Transient var a: Int = -1, var b: Int = -1, @Transient var c: Int = -1)
|
||||||
|
|
||||||
@Test fun transientProperty() {
|
@Test fun transientProperty() {
|
||||||
val moshi = Moshi.Builder().build()
|
val moshi = Moshi.Builder().build()
|
||||||
val jsonAdapter = moshi.adapter<TransientProperty>()
|
val jsonAdapter = moshi.adapter<TransientProperty>()
|
||||||
|
@@ -294,6 +294,21 @@ class KotlinJsonAdapterTest {
|
|||||||
|
|
||||||
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
|
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
|
||||||
|
|
||||||
|
@Test fun multipleTransientConstructorParameters() {
|
||||||
|
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||||
|
val jsonAdapter = moshi.adapter(MultipleTransientConstructorParameters::class.java)
|
||||||
|
|
||||||
|
val encoded = MultipleTransientConstructorParameters(3, 5, 7)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(-1)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
assertThat(decoded.c).isEqualTo(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultipleTransientConstructorParameters(@Transient var a: Int = -1, var b: Int = -1, @Transient var c: Int = -1)
|
||||||
|
|
||||||
@Test fun requiredTransientConstructorParameterFails() {
|
@Test fun requiredTransientConstructorParameterFails() {
|
||||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||||
try {
|
try {
|
||||||
|
Reference in New Issue
Block a user