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
This commit is contained in:
jwilson
2017-05-07 16:18:12 -04:00
parent e59dbf4f96
commit 494992dab8
2 changed files with 59 additions and 15 deletions

View File

@@ -115,12 +115,6 @@ internal class KotlinJsonAdapter<T>(
val adapter: JsonAdapter<P>, val adapter: JsonAdapter<P>,
val property: KProperty1<K, P>, val property: KProperty1<K, P>,
val parameter: KParameter?) { 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 get(value: K) = property.get(value)
fun set(result: K, value: P) { fun set(result: K, value: P) {
@@ -171,11 +165,13 @@ object KotlinJsonAdapterFactory : JsonAdapter.Factory {
for (property in rawType.kotlin.memberProperties) { for (property in rawType.kotlin.memberProperties) {
if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) continue if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) continue
val parameter = parametersByName[property.name]
if (property !is KMutableProperty1 && parameter == null) continue
property.isAccessible = true property.isAccessible = true
var allAnnotations = property.annotations var allAnnotations = property.annotations
var jsonAnnotation = property.findAnnotation<Json>() var jsonAnnotation = property.findAnnotation<Json>()
val parameter = parametersByName[property.name]
if (parameter != null) { if (parameter != null) {
allAnnotations += parameter.annotations allAnnotations += parameter.annotations
if (jsonAnnotation == null) { if (jsonAnnotation == null) {

View File

@@ -445,15 +445,17 @@ class KotlinJsonAdapterTest {
} }
} }
@Test fun unsettableProperty() { @Test fun unsettablePropertyIgnored() {
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build()
try { val jsonAdapter = moshi.adapter(UnsettableProperty::class.java)
moshi.adapter(UnsettableProperty::class.java)
fail() val encoded = UnsettableProperty()
} catch(expected: IllegalArgumentException) { encoded.b = 5
assertThat(expected).hasMessage("No constructor or var property for " + assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
"val ${UnsettableProperty::class.qualifiedName}.a: kotlin.Int")
} val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6)
} }
class UnsettableProperty { class UnsettableProperty {
@@ -461,6 +463,52 @@ class KotlinJsonAdapterTest {
var b: Int = -1 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() { @Test fun nonPropertyConstructorParameter() {
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build() val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory).build()
try { try {