v1.0.5-SNAPSHOT started json and kotlinx serialization support

This commit is contained in:
sergeych committed 2025-12-04 17:05:07 +01:00
1 parent 5cfc15cf17
commit 171e413c5f
12 files changed
+140 -4

No files matched your search

@@ -17,8 +17,13 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.serializer
import net.sergeych.lyng.*
import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
@@ -376,6 +381,10 @@ open class Obj {
return null
}
open suspend fun toJson(scope: Scope = Scope()): JsonElement {
scope.raiseNotImplemented("toJson for ${objClass.className}")
}
companion object {
val rootObjectType = ObjClass("Obj").apply {
@@ -418,7 +427,9 @@ open class Obj {
thisObj.putAt(this, requiredArg<Obj>(0), newValue)
newValue
}
addFn("toJsonString") {
thisObj.toJson(this).toString().toObj()
}
}
@@ -520,6 +531,10 @@ object ObjNull : Obj() {
}
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonNull
}
override val objClass: ObjClass by lazy {
object : ObjClass("Null") {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
@@ -572,4 +587,25 @@ data class ObjNamespace(val name: String) : Obj() {
}
}
/**
* Decodes the current object into a deserialized form using the provided deserialization strategy.
* It is based on [Obj.toJson] and uses existing Kotlin Json serialization, without string representation
* (only `JsonElement` to carry information between Kotlin and Lyng serialization worlds), thus efficient.
*
* @param strategy The deserialization strategy that defines how the object should be decoded.
* @param scope An optional scope used during deserialization to define the context. Defaults to a new instance of Scope.
* @return The deserialized object of type T.
*/
suspend fun <T>Obj.decodeSerializableWith(strategy: DeserializationStrategy<T>, scope: Scope = Scope()): T =
Json.decodeFromJsonElement(strategy,toJson(scope))
/**
* Decodes a serializable object of type [T] using the provided decoding scope. The deserialization uses
* [Obj.toJson] and existing Json based serialization ithout using actual string representation, thus
* efficient.
*
* @param T The type of the object to be decoded. Must be a reified type.
* @param scope The scope used during decoding. Defaults to a new instance of [Scope].
*/
suspend inline fun <reified T>Obj.decodeSerializable(scope: Scope= Scope()) =
decodeSerializableWith<T>(serializer<T>(), scope)
@@ -17,6 +17,8 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Scope
import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
@@ -62,6 +64,10 @@ data class ObjBool(val value: Boolean) : Obj() {
return value == other.value
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonPrimitive(value)
}
companion object {
val type = object : ObjClass("Bool") {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder,lynonType: LynonType?): Obj {
@@ -17,6 +17,8 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Scope
import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
@@ -161,6 +163,10 @@ class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Nu
}
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonPrimitive(value)
}
companion object {
val Zero = ObjInt(0, true)
val One = ObjInt(1, true)
@@ -17,6 +17,8 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.statement
@@ -188,6 +190,10 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
override suspend fun lynonType(): LynonType = LynonType.List
override suspend fun toJson(scope: Scope): JsonElement {
return JsonArray(list.map { it.toJson(scope) })
}
companion object {
val type = object : ObjClass("List", ObjArray) {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
@@ -17,6 +17,8 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lynon.LynonDecoder
@@ -141,6 +143,12 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
encoder.encodeAnyList(scope, values, fixedSize = true)
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonObject(
map.map { it.key.toString(scope).value to it.value.toJson(scope) }.toMap()
)
}
companion object {
suspend fun listToMap(scope: Scope, list: List<Obj>): MutableMap<Obj, Obj> {
@@ -17,6 +17,8 @@
package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope
import net.sergeych.lyng.statement
@@ -100,6 +102,10 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
encoder.encodeReal(value)
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonPrimitive(value)
}
companion object {
val type: ObjClass = object : ObjClass("Real") {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
@@ -19,6 +19,8 @@ package net.sergeych.lyng.obj
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.PerfFlags
import net.sergeych.lyng.RegexCache
import net.sergeych.lyng.Scope
@@ -116,6 +118,10 @@ data class ObjString(val value: String) : Obj() {
encoder.encodeBinaryData(value.encodeToByteArray())
}
override suspend fun toJson(scope: Scope): JsonElement {
return JsonPrimitive(value)
}
companion object {
val type = object : ObjClass("String") {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.Serializable
import net.sergeych.lyng.*
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.InlineSourcesImportProvider
@@ -3797,8 +3798,21 @@ class ScriptTest {
"""
)
}
@Serializable
data class JSTest1(val foo: String,val one: Int, val ok: Boolean)
@Test
fun testToJson() = runTest {
val x = eval("""{ "foo": "bar", "one": 1, "ok": true }""")
println(x.toJson())
assertEquals(x.toJson().toString(), """{"foo":"bar","one":1,"ok":true}""")
assertEquals(
(eval("""{ "foo": "bar", "one": 1, "ok": true }.toJsonString()""") as ObjString).value,
"""{"foo":"bar","one":1,"ok":true}""")
println(x.decodeSerializable<JSTest1>())
assertEquals(JSTest1("bar", 1, true), x.decodeSerializable<JSTest1>())
}
}
+5
View File
@@ -352,4 +352,9 @@ class BookTest {
fun testRegex() = runBlocking {
runDocTests("../docs/Regex.md")
}
@Test
fun testJson() = runBlocking {
runDocTests("../docs/json_and_kotlin_serialization.md")
}
}