All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.pylons.lib.JsonModelSerializer.kt Maven / Gradle / Ivy

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
            }

        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy