From 494992dab8d56b7a9701933e5972903387d6c33b Mon Sep 17 00:00:00 2001 From: jwilson Date: Sun, 7 May 2017 16:18:12 -0400 Subject: [PATCH] Support more kinds of properties in KotlinJsonAdapter This makes it possible to have synthetic properties that have no state. Also test properties that are synthetic and have no backing field. Closes: https://github.com/square/moshi/issues/299 --- .../com/squareup/moshi/KotlinJsonAdapter.kt | 10 +-- .../squareup/moshi/KotlinJsonAdapterTest.kt | 64 ++++++++++++++++--- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt index 77a689e..bdecb4e 100644 --- a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt +++ b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt @@ -115,12 +115,6 @@ internal class KotlinJsonAdapter( val adapter: JsonAdapter

, val property: KProperty1, val parameter: KParameter?) { - init { - if (property !is KMutableProperty1 && parameter == null) { - throw IllegalArgumentException("No constructor or var property for ${property}") - } - } - fun get(value: K) = property.get(value) fun set(result: K, value: P) { @@ -171,11 +165,13 @@ object KotlinJsonAdapterFactory : JsonAdapter.Factory { for (property in rawType.kotlin.memberProperties) { if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) continue + val parameter = parametersByName[property.name] + if (property !is KMutableProperty1 && parameter == null) continue + property.isAccessible = true var allAnnotations = property.annotations var jsonAnnotation = property.findAnnotation() - val parameter = parametersByName[property.name] if (parameter != null) { allAnnotations += parameter.annotations if (jsonAnnotation == null) { diff --git a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt index e3e87d8..9b4ce08 100644 --- a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt +++ b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt @@ -445,15 +445,17 @@ class KotlinJsonAdapterTest { } } - @Test fun unsettableProperty() { + @Test fun unsettablePropertyIgnored() { val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() - try { - moshi.adapter(UnsettableProperty::class.java) - fail() - } catch(expected: IllegalArgumentException) { - assertThat(expected).hasMessage("No constructor or var property for " + - "val ${UnsettableProperty::class.qualifiedName}.a: kotlin.Int") - } + val jsonAdapter = moshi.adapter(UnsettableProperty::class.java) + + val encoded = UnsettableProperty() + encoded.b = 5 + 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) } class UnsettableProperty { @@ -461,6 +463,52 @@ class KotlinJsonAdapterTest { var b: Int = -1 } + @Test fun getterOnlyNoBackingField() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() + val jsonAdapter = moshi.adapter(GetterOnly::class.java) + + val encoded = GetterOnly(3, 5) + assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") + + val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!! + assertThat(decoded.a).isEqualTo(4) + assertThat(decoded.b).isEqualTo(6) + assertThat(decoded.total).isEqualTo(10) + } + + class GetterOnly(var a: Int, var b: Int) { + val total : Int + get() = a + b + } + + @Test fun getterAndSetterNoBackingField() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() + val jsonAdapter = moshi.adapter(GetterAndSetter::class.java) + + val encoded = GetterAndSetter(3, 5) + assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5,\"total\":8}") + + // Whether b is 6 or 7 is an implementation detail. Currently we call constructors then setters. + val decoded1 = jsonAdapter.fromJson("{\"a\":4,\"b\":6,\"total\":11}")!! + assertThat(decoded1.a).isEqualTo(4) + assertThat(decoded1.b).isEqualTo(7) + assertThat(decoded1.total).isEqualTo(11) + + // Whether b is 6 or 7 is an implementation detail. Currently we call constructors then setters. + val decoded2 = jsonAdapter.fromJson("{\"a\":4,\"total\":11,\"b\":6}")!! + assertThat(decoded2.a).isEqualTo(4) + assertThat(decoded2.b).isEqualTo(7) + assertThat(decoded2.total).isEqualTo(11) + } + + class GetterAndSetter(var a: Int, var b: Int) { + var total : Int + get() = a + b + set(value) { + b = value - a + } + } + @Test fun nonPropertyConstructorParameter() { val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() try {