Skip to content

Commit 381b0f0

Browse files
committed
handle function and lazy properties when filling json object
1 parent a94ff4d commit 381b0f0

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

src/main/kotlin/com/github/jsonj/JsonJExtensions.kt

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import kotlin.reflect.KClass
1212
import kotlin.reflect.KParameter
1313
import kotlin.reflect.KType
1414
import kotlin.reflect.full.cast
15+
import kotlin.reflect.full.declaredMemberProperties
1516
import kotlin.reflect.full.isSubtypeOf
16-
import kotlin.reflect.full.memberProperties
1717
import kotlin.reflect.full.primaryConstructor
1818
import kotlin.reflect.full.starProjectedType
1919
import kotlin.reflect.full.withNullability
@@ -50,8 +50,9 @@ fun JsonObject.arrayField(key: String, vararg values: Any) {
5050
* @return the value of the first field matching the name or null
5151
*/
5252
fun JsonObject.flexGet(name: String, ignoreCase: Boolean = true, ignoreUnderscores: Boolean = true): JsonElement? {
53-
val key = keys.filter { normalize(it, ignoreCase, ignoreUnderscores) == normalize(name, ignoreCase, ignoreUnderscores) }
54-
.firstOrNull()
53+
val key =
54+
keys.filter { normalize(it, ignoreCase, ignoreUnderscores) == normalize(name, ignoreCase, ignoreUnderscores) }
55+
.firstOrNull()
5556
return if (key != null) {
5657
val value = get(key)
5758
if (value?.isNull() == true) {
@@ -120,19 +121,30 @@ fun <T : Any> JsonObject.construct(clazz: KClass<T>): T {
120121
fun <T : Any> JsonObject.fill(obj: T): JsonObject {
121122
val clazz = obj::class
122123

123-
for (memberProperty in clazz.memberProperties) {
124+
for (memberProperty in clazz.declaredMemberProperties) {
124125
val propertyName = memberProperty.name
125126
val jsonName = toUnderscore(propertyName)
126127

127-
val value = memberProperty.getter.call(obj)
128-
if (memberProperty.returnType.isSubtypeOf(Enum::class.starProjectedType)) {
129-
val enumValue = value as Enum<*>
130-
put(jsonName, enumValue.name)
131-
} else {
132-
val returnType = memberProperty.returnType
133-
val jsonElement: JsonElement = jsonElement(returnType, value)
128+
try {
129+
val value = memberProperty.getter.call(obj)
130+
if (memberProperty.returnType.isSubtypeOf(Enum::class.starProjectedType)) {
131+
val enumValue = value as Enum<*>
132+
put(jsonName, enumValue.name)
133+
} else {
134+
val returnType = memberProperty.returnType
135+
val jsonElement: JsonElement = jsonElement(returnType, value)
134136

135-
put(jsonName, jsonElement)
137+
put(jsonName, jsonElement)
138+
}
139+
} catch (e: UnsupportedOperationException) {
140+
// function properties fail, skip those
141+
if (!(e.message?.contains("internal synthetic class") ?: false)) {
142+
throw e
143+
} else {
144+
@Suppress("UNCHECKED_CAST") // this seems to work ;-), ugly though
145+
val fn = (memberProperty.call(obj) ?: throw e) as Function0<Any>
146+
put(jsonName, fn.invoke())
147+
}
136148
}
137149
}
138150
return this

src/test/kotlin/com/github/jsonj/JsonJAdaptableTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ data class Foo(
1919
val value6: BigDecimal,
2020
val maybe: Boolean,
2121
val numnun: MyEnum
22-
) : JsonJAdaptable
22+
) : JsonJAdaptable {
23+
val synthetic = { message.reversed() }
24+
val lzy by lazy { message.reversed() }
25+
}
2326

2427
class JsonJAdaptableTest {
2528
@Test
@@ -38,6 +41,9 @@ class JsonJAdaptableTest {
3841
numnun = MyEnum.FOO
3942
)
4043
val json = foo.asJsonObject()
44+
println(json.prettyPrint())
45+
// extra fields get ignored
46+
json.put("idontexistasafield", 42)
4147
val reconstructed = json.construct(Foo::class)
4248
assertThat(foo.message2).isNotBlank()
4349
assertThat(foo.message3).isNull()

0 commit comments

Comments
 (0)