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

commonMain.dev.evo.elasticmagic.serde.serialization.JsonDeserializer.kt Maven / Gradle / Ivy

package dev.evo.elasticmagic.serde.kotlinx

import dev.evo.elasticmagic.serde.DeserializationException
import dev.evo.elasticmagic.serde.Deserializer

import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.floatOrNull
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.longOrNull

import kotlin.jvm.JvmInline

sealed class JsonDeserializer : Deserializer {
    companion object : JsonDeserializer() {
        private fun coerceToAny(v: JsonElement): Any? {
            return when (v) {
                is JsonNull -> null
                is JsonPrimitive -> {
                    if (v.isString) {
                        v.content
                    } else {
                        v.longOrNull
                            ?: v.doubleOrNull
                            ?: v.booleanOrNull
                    }
                }
                is JsonObject -> ObjectCtx(v)
                is JsonArray -> ArrayCtx(v)
            }
        }
    }

    @JvmInline
    value class ObjectCtx(private val obj: JsonObject) : Deserializer.ObjectCtx {
        override fun anyOrNull(name: String): Any? {
            return obj[name]?.let(::coerceToAny)
        }

        override fun intOrNull(name: String): Int? {
            return obj[name]?.jsonPrimitive?.intOrNull
        }

        override fun longOrNull(name: String): Long? {
            return obj[name]?.jsonPrimitive?.longOrNull
        }

        override fun floatOrNull(name: String): Float? {
            return obj[name]?.jsonPrimitive?.floatOrNull
        }

        override fun doubleOrNull(name: String): Double? {
            return obj[name]?.jsonPrimitive?.doubleOrNull
        }

        override fun booleanOrNull(name: String): Boolean? {
            return obj[name]?.jsonPrimitive?.booleanOrNull
        }

        override fun stringOrNull(name: String): String? {
            return obj[name]?.jsonPrimitive?.contentOrNull
        }

        override fun objOrNull(name: String): Deserializer.ObjectCtx? {
            return (obj[name] as? JsonObject)?.let(JsonDeserializer::ObjectCtx)
        }

        override fun arrayOrNull(name: String): Deserializer.ArrayCtx? {
            return (obj[name] as? JsonArray)?.let(JsonDeserializer::ArrayCtx)
        }

        override fun iterator(): ObjectIterator {
            return ObjectIterator(obj.iterator())
        }

        fun forEachAny(block: (String, Any) -> Unit) {
            for (entry in obj) {
                block(entry.key, coerceToAny(entry.value) ?: error("Missing a value"))
            }
        }
    }

    class ObjectIterator(
        private val iter: Iterator>
    ) : Deserializer.ObjectIterator {
        private var currentEntry: Map.Entry? = null

        override fun hasNext(): Boolean {
            return iter.hasNext().also {
                currentEntry = if (it) iter.next() else null
            }
        }

        private fun getCurrentEntry(): Map.Entry {
            return currentEntry ?: throw IllegalStateException("hasNext must be called first")
        }

        override fun key(): String = getCurrentEntry().key

        private fun value(): JsonElement = getCurrentEntry().value

        override fun anyOrNull(): Any? {
            return coerceToAny(value())
        }

        override fun intOrNull(): Int? {
            return value().jsonPrimitiveOrNull?.intOrNull
        }

        override fun longOrNull(): Long? {
            return value().jsonPrimitiveOrNull?.longOrNull
        }

        override fun floatOrNull(): Float? {
            return value().jsonPrimitiveOrNull?.floatOrNull
        }

        override fun doubleOrNull(): Double? {
            return value().jsonPrimitiveOrNull?.doubleOrNull
        }

        override fun booleanOrNull(): Boolean? {
            return value().jsonPrimitiveOrNull?.booleanOrNull
        }

        override fun stringOrNull(): String? {
            return value().jsonPrimitiveOrNull?.contentOrNull
        }

        override fun objOrNull(): Deserializer.ObjectCtx? {
            return value().jsonObjectOrNull?.let(JsonDeserializer::ObjectCtx)
        }

        override fun arrayOrNull(): Deserializer.ArrayCtx? {
            return value().jsonArrayOrNull?.let(JsonDeserializer::ArrayCtx)
        }
    }

    @JvmInline
    value class ArrayCtx(private val array: JsonArray) : Deserializer.ArrayCtx {
        override fun iterator(): Deserializer.ArrayIterator {
            return ArrayIterator(array.iterator())
        }
    }

    class ArrayIterator(private val iter: Iterator) : Deserializer.ArrayIterator {
        private var currentValue: JsonElement? = null

        private fun getCurrentValue(): JsonElement {
            return currentValue ?: throw IllegalStateException("hasNext must be called first")
        }

        private fun nextPrimitive(): JsonPrimitive? {
            return when(val v = currentValue) {
                is JsonPrimitive -> v
                else -> null
            }
        }

        override fun hasNext(): Boolean {
            return iter.hasNext().also {
                currentValue = if (it) iter.next() else null
            }
        }

        override fun anyOrNull(): Any? {
            return coerceToAny(getCurrentValue())
        }

        override fun intOrNull(): Int? {
            return nextPrimitive()?.intOrNull
        }

        override fun longOrNull(): Long? {
            return nextPrimitive()?.longOrNull
        }

        override fun floatOrNull(): Float? {
            return nextPrimitive()?.floatOrNull
        }

        override fun doubleOrNull(): Double? {
            return nextPrimitive()?.doubleOrNull
        }

        override fun booleanOrNull(): Boolean? {
            return nextPrimitive()?.booleanOrNull
        }

        override fun stringOrNull(): String? {
            return nextPrimitive()?.contentOrNull
        }

        override fun objOrNull(): Deserializer.ObjectCtx? {
            return (getCurrentValue() as? JsonObject)?.let(JsonDeserializer::ObjectCtx)
        }

        override fun arrayOrNull(): Deserializer.ArrayCtx? {
            return (getCurrentValue() as? JsonArray)?.let(JsonDeserializer::ArrayCtx)
        }
    }

    override fun objFromStringOrNull(data: String): Deserializer.ObjectCtx? {
        val jsonObj =  try {
            Json.decodeFromString(JsonElement.serializer(), data) as? JsonObject
        } catch (e: SerializationException) {
            throw DeserializationException("Cannot deserialize data", e)
        }
        return jsonObj?.let(::ObjectCtx)
    }
}

private val JsonElement.jsonPrimitiveOrNull: JsonPrimitive?
    get() = when (this) {
        is JsonNull -> null
        is JsonPrimitive -> this
        else -> null
    }

private val JsonElement.jsonObjectOrNull: JsonObject?
    get() = this as? JsonObject

private val JsonElement.jsonArrayOrNull: JsonArray?
    get() = this as? JsonArray




© 2015 - 2025 Weber Informatics LLC | Privacy Policy