Skip to content

Commit

Permalink
handle function and lazy properties when filling json object
Browse files Browse the repository at this point in the history
  • Loading branch information
jillesvangurp committed Oct 26, 2018
1 parent a94ff4d commit 381b0f0
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 13 deletions.
36 changes: 24 additions & 12 deletions src/main/kotlin/com/github/jsonj/JsonJExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.full.cast
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.full.withNullability
Expand Down Expand Up @@ -50,8 +50,9 @@ fun JsonObject.arrayField(key: String, vararg values: Any) {
* @return the value of the first field matching the name or null
*/
fun JsonObject.flexGet(name: String, ignoreCase: Boolean = true, ignoreUnderscores: Boolean = true): JsonElement? {
val key = keys.filter { normalize(it, ignoreCase, ignoreUnderscores) == normalize(name, ignoreCase, ignoreUnderscores) }
.firstOrNull()
val key =
keys.filter { normalize(it, ignoreCase, ignoreUnderscores) == normalize(name, ignoreCase, ignoreUnderscores) }
.firstOrNull()
return if (key != null) {
val value = get(key)
if (value?.isNull() == true) {
Expand Down Expand Up @@ -120,19 +121,30 @@ fun <T : Any> JsonObject.construct(clazz: KClass<T>): T {
fun <T : Any> JsonObject.fill(obj: T): JsonObject {
val clazz = obj::class

for (memberProperty in clazz.memberProperties) {
for (memberProperty in clazz.declaredMemberProperties) {
val propertyName = memberProperty.name
val jsonName = toUnderscore(propertyName)

val value = memberProperty.getter.call(obj)
if (memberProperty.returnType.isSubtypeOf(Enum::class.starProjectedType)) {
val enumValue = value as Enum<*>
put(jsonName, enumValue.name)
} else {
val returnType = memberProperty.returnType
val jsonElement: JsonElement = jsonElement(returnType, value)
try {
val value = memberProperty.getter.call(obj)
if (memberProperty.returnType.isSubtypeOf(Enum::class.starProjectedType)) {
val enumValue = value as Enum<*>
put(jsonName, enumValue.name)
} else {
val returnType = memberProperty.returnType
val jsonElement: JsonElement = jsonElement(returnType, value)

put(jsonName, jsonElement)
put(jsonName, jsonElement)
}
} catch (e: UnsupportedOperationException) {
// function properties fail, skip those
if (!(e.message?.contains("internal synthetic class") ?: false)) {
throw e
} else {
@Suppress("UNCHECKED_CAST") // this seems to work ;-), ugly though
val fn = (memberProperty.call(obj) ?: throw e) as Function0<Any>
put(jsonName, fn.invoke())
}
}
}
return this
Expand Down
8 changes: 7 additions & 1 deletion src/test/kotlin/com/github/jsonj/JsonJAdaptableTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ data class Foo(
val value6: BigDecimal,
val maybe: Boolean,
val numnun: MyEnum
) : JsonJAdaptable
) : JsonJAdaptable {
val synthetic = { message.reversed() }
val lzy by lazy { message.reversed() }
}

class JsonJAdaptableTest {
@Test
Expand All @@ -38,6 +41,9 @@ class JsonJAdaptableTest {
numnun = MyEnum.FOO
)
val json = foo.asJsonObject()
println(json.prettyPrint())
// extra fields get ignored
json.put("idontexistasafield", 42)
val reconstructed = json.construct(Foo::class)
assertThat(foo.message2).isNotBlank()
assertThat(foo.message3).isNull()
Expand Down

0 comments on commit 381b0f0

Please sign in to comment.