tech.pylons.lib.JsonModelSerializer.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of libpylons Show documentation
Show all versions of libpylons Show documentation
Library providing common functionality for interacting with the Pylons ecosystem
The newest version!
package tech.pylons.lib
import com.beust.klaxon.Json
import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import kotlin.reflect.KProperty1
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
object JsonModelSerializer {
fun serialize (mode : SerializationMode, obj: Any?) : String {
val jo = processObject(mode, obj)
return jo?.toJsonString(
prettyPrint = mode == SerializationMode.FOR_BROADCAST,
canonical = true
).orEmpty().replace("{}", "null")
// HACK! this is used to not serialize empty maps atm.
// TODO: write proper map handling so we don't need Bullshit here
}
private fun processObject (mode : SerializationMode, obj: Any?) : JsonObject? {
if (obj != null) {
val kClass = obj::class
val o = JsonObject()
kClass.memberProperties.forEach { prop ->
val json = prop.findAnnotation()
if (json != null) {
var value = prop.getter.call(obj)
if (prop.returnType == String::class.java) value = value.toString()
when (prop.returnType.classifier) {
// HACK: klaxon's serialization of byte values is actually broken!
// We have to do this b/c if we just set value w/o doing the Bullshit
// it'll be serialized as... an empty object. Thanks, klaxon!
Byte::class -> o[json.name] = (value as Byte).toInt()
Int::class -> {
val q = prop.findAnnotation()
val n = prop.findAnnotation()
if ((q != null || mode == SerializationMode.FOR_BROADCAST) && n == null)
o[json.name] = (value as Int).toString()
else o[json.name] = value
}
Long::class -> {
val q = prop.findAnnotation()
val n = prop.findAnnotation()
if ((q != null || mode == SerializationMode.FOR_BROADCAST) && n == null)
o[json.name] = (value as Long).toString()
else o[json.name] = value
}
Number::class -> o[json.name] = value
Float::class -> o[json.name] = value
Double::class -> o[json.name] = value
Boolean::class -> o[json.name] = value
String::class -> o[json.name] = value
else -> o[json.name] = handleComplexValues(mode, prop, value)
}
}
}
return o
}
else return null
}
private fun handleComplexValues (mode : SerializationMode, prop : KProperty1, value : Any?) : Any? {
return when {
value == null -> null
Regex("kotlin.Array<.*>").matches(prop.returnType.toString()) -> handleArrays(mode, prop, value)
Regex("kotlin.collections.List<.*>").matches(prop.returnType.toString()) -> handleLists(mode, prop, value)
Regex("kotlin.collections.Map<.*>").matches(prop.returnType.toString()) -> handleMaps(mode, prop, value)
else -> processObject(mode, value) // serialize nested object
}
}
private fun numeralArrayElement (mode: SerializationMode, it : T, q : QuotedJsonNumeral?, arr: JsonArray) {
if (q != null && (q.serializationMode == SerializationMode.ALL || q.serializationMode == mode))
arr.add(it.toString())
else arr.add(it)
}
private fun handleArrays (mode : SerializationMode, prop : KProperty1, value : Any?) : JsonArray<*>? {
if ((value as Array<*>).size == 0) return null
else {
val jsonArray = JsonArray()
val q = prop.findAnnotation()
when (prop.returnType.toString()) {
"kotlin.Array" -> (value as Array).forEach { jsonArray.add(it) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.Array" -> (value as Array).forEach { jsonArray.add(it) }
else -> value.forEach { jsonArray.add(processObject(mode, it)) }
}
return jsonArray
}
}
private fun handleLists (mode : SerializationMode, prop : KProperty1, value : Any?) : JsonArray<*>? {
if ((value as List<*>).size == 0) {
return if (prop.findAnnotation() != null) {
JsonArray()
} else {
null
}
}
else {
val jsonArray = JsonArray()
val q = prop.findAnnotation()
when (prop.returnType.toString()) {
"kotlin.collections.List" -> (value as List).forEach { jsonArray.add(it) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { numeralArrayElement(mode, it, q, jsonArray) }
"kotlin.collections.List" -> (value as List).forEach { jsonArray.add(it) }
else -> value.forEach { jsonArray.add(processObject(mode, it)) }
}
return jsonArray
}
}
private fun handleMaps(mode : SerializationMode, prop : KProperty1, value : Any?): JsonArray<*>? {
if ((value as Map).size == 0) return null
else {
val jsonArray = JsonArray()
val q = prop.findAnnotation()
when (prop.returnType.toString()) {
"kotlin.collections.Map" -> (value as Map).forEach {
val obj = JsonObject()
obj["Key"] = it.key
// broadcast has to be string format "1" while signing has to be number 1
obj["Value"] = if (mode == SerializationMode.FOR_BROADCAST) {
it.value.toString()
} else {
it.value
}
jsonArray.add(obj)
}
"kotlin.collections.Map" -> (value as Map).forEach {
val obj = JsonObject()
obj["Key"] = it.key
obj["Value"] = it.value
jsonArray.add(obj)
}
else -> value.forEach {
val obj = JsonObject()
obj["Key"] = it.key
obj["Value"] = if (q != null && (q.serializationMode == SerializationMode.ALL || q.serializationMode == mode)) {
it.value.toString()
} else {
it.value
}
jsonArray.add(obj)
}
}
return if (jsonArray.isNotEmpty()) {
jsonArray
} else {
// must return null for empty array
null
}
}
}
}