Switched to kotlinx serialization for json processing
This commit is contained in:
parent
d3ce88eb3f
commit
3d65a1b409
@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package hep.dataforge.context
|
package hep.dataforge.context
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.buildMeta
|
||||||
|
import hep.dataforge.names.toName
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package hep.dataforge.context
|
package hep.dataforge.context
|
||||||
|
|
||||||
|
import hep.dataforge.meta.*
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'kotlin-multiplatform'
|
id 'kotlin-multiplatform'
|
||||||
//id 'kotlinx-serialization'
|
id 'kotlinx-serialization'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -21,7 +21,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api project(":dataforge-meta")
|
api project(":dataforge-meta")
|
||||||
//implementation 'org.jetbrains.kotlin:kotlin-reflect'
|
//implementation 'org.jetbrains.kotlin:kotlin-reflect'
|
||||||
//implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,8 +33,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.cliftonlabs:json-simple:3.0.2'
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
|
||||||
//implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +45,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
//implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
package hep.dataforge.meta.io
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.values.*
|
||||||
|
import kotlinx.io.core.Input
|
||||||
|
import kotlinx.io.core.Output
|
||||||
|
import kotlinx.io.core.readText
|
||||||
|
import kotlinx.io.core.writeText
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
|
||||||
|
|
||||||
|
object JSONMetaFormat : MetaFormat {
|
||||||
|
override val name: String = "json"
|
||||||
|
override val key: Short = 0x4a53//"JS"
|
||||||
|
|
||||||
|
override fun write(meta: Meta, out: Output) {
|
||||||
|
val str = meta.toJson().toString()
|
||||||
|
out.writeText(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(input: Input): Meta {
|
||||||
|
val str = input.readText()
|
||||||
|
val json = JsonTreeParser.parse(str)
|
||||||
|
return json.toMeta()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Value.toJson(): JsonElement {
|
||||||
|
return when (type) {
|
||||||
|
ValueType.NUMBER -> JsonPrimitive(number)
|
||||||
|
ValueType.STRING -> JsonPrimitive(string)
|
||||||
|
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
||||||
|
ValueType.NULL -> JsonNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Meta.toJson(): JsonObject {
|
||||||
|
val map = this.items.mapValues { entry ->
|
||||||
|
val value = entry.value
|
||||||
|
when (value) {
|
||||||
|
is MetaItem.ValueItem -> value.value.toJson()
|
||||||
|
is MetaItem.NodeItem -> value.node.toJson()
|
||||||
|
}
|
||||||
|
}.mapKeys { it.key.toString() }
|
||||||
|
return JsonObject(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonObject.toMeta() = JsonMeta(this)
|
||||||
|
|
||||||
|
private fun JsonPrimitive.toValue(): Value {
|
||||||
|
return when (this) {
|
||||||
|
JsonNull -> Null
|
||||||
|
else -> this.content.parseValue() // Optimize number and boolean parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonMeta(val json: JsonObject) : Meta {
|
||||||
|
override val items: Map<NameToken, MetaItem<out Meta>> by lazy {
|
||||||
|
json.mapKeys { NameToken(it.key) }.mapValues { entry ->
|
||||||
|
val element = entry.value
|
||||||
|
when (element) {
|
||||||
|
is JsonPrimitive -> MetaItem.ValueItem<JsonMeta>(element.toValue())
|
||||||
|
is JsonObject -> MetaItem.NodeItem(element.toMeta())
|
||||||
|
is JsonArray -> {
|
||||||
|
if (element.all { it is JsonPrimitive }) {
|
||||||
|
val value = ListValue(element.map { (it as JsonPrimitive).toValue() })
|
||||||
|
MetaItem.ValueItem<JsonMeta>(value)
|
||||||
|
} else {
|
||||||
|
TODO("mixed nodes json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,28 +15,16 @@ interface MetaFormat {
|
|||||||
fun read(input: Input): Meta
|
fun read(input: Input): Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MetaFormat.stringify(meta: Meta): String {
|
fun Meta.asString(format: MetaFormat = JSONMetaFormat): String{
|
||||||
val builder = BytePacketBuilder()
|
val builder = BytePacketBuilder()
|
||||||
write(meta,builder)
|
format.write(this,builder)
|
||||||
return builder.build().readText()
|
return builder.build().readText()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Meta.asString() = JSONMetaFormat.stringify(this)
|
|
||||||
|
|
||||||
fun MetaFormat.parse(str: String): Meta{
|
fun MetaFormat.parse(str: String): Meta{
|
||||||
return read(ByteReadPacket(str.toByteArray()))
|
return read(ByteReadPacket(str.toByteArray()))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal expect fun writeJson(meta: Meta, out: Output)
|
|
||||||
internal expect fun readJson(input: Input, length: Int = -1): Meta
|
|
||||||
|
|
||||||
object JSONMetaFormat : MetaFormat {
|
|
||||||
override val name: String = "json"
|
|
||||||
override val key: Short = 0x4a53//"JS"
|
|
||||||
|
|
||||||
override fun write(meta: Meta, out: Output) = writeJson(meta, out)
|
|
||||||
override fun read(input: Input): Meta = readJson(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
object BinaryMetaFormat : MetaFormat {
|
object BinaryMetaFormat : MetaFormat {
|
||||||
override val name: String = "bin"
|
override val name: String = "bin"
|
||||||
|
@ -14,7 +14,7 @@ class MetaFormatTest{
|
|||||||
"c" to 11.1
|
"c" to 11.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val string = BinaryMetaFormat.stringify(meta)
|
val string = meta.asString(BinaryMetaFormat)
|
||||||
val result = BinaryMetaFormat.parse(string)
|
val result = BinaryMetaFormat.parse(string)
|
||||||
assertEquals(meta,result)
|
assertEquals(meta,result)
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ class MetaFormatTest{
|
|||||||
"c" to 11.1
|
"c" to 11.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val string = JSONMetaFormat.stringify(meta)
|
val string = meta.asString(JSONMetaFormat)
|
||||||
val result = JSONMetaFormat.parse(string)
|
val result = JSONMetaFormat.parse(string)
|
||||||
assertEquals(meta,result)
|
assertEquals(meta,result)
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package hep.dataforge.meta.io
|
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.names.NameToken
|
|
||||||
import hep.dataforge.values.Value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent any js object as meta
|
|
||||||
*/
|
|
||||||
class JSMeta(val obj: Any) : Meta {
|
|
||||||
override val items: Map<NameToken, MetaItem<out Meta>>
|
|
||||||
get() = listKeys(obj).map { NameToken(it) }.associateWith { convert(js("obj[it]")) }
|
|
||||||
|
|
||||||
private fun listKeys(obj: Any): List<String> = js("Object").keys(obj) as List<String>
|
|
||||||
|
|
||||||
private fun isList(obj: Any): Boolean = js("Array").isArray(obj) as Boolean
|
|
||||||
|
|
||||||
private fun isPrimitive(@Suppress("UNUSED_PARAMETER") obj: Any?): Boolean = js("obj !== Object(obj)") as Boolean
|
|
||||||
|
|
||||||
private fun convert(obj: Any?): MetaItem<out Meta> {
|
|
||||||
return when (obj) {
|
|
||||||
null, isPrimitive(obj), is Number, is String, is Boolean -> MetaItem.ValueItem<JSMeta>(Value.of(obj))
|
|
||||||
isList(obj) -> {
|
|
||||||
val list = obj as List<*>
|
|
||||||
MetaItem.ValueItem<JSMeta>(Value.of(list))
|
|
||||||
}
|
|
||||||
else -> MetaItem.NodeItem(JSMeta(obj))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package hep.dataforge.meta.io
|
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import kotlinx.io.core.Output
|
|
||||||
import kotlinx.io.core.readText
|
|
||||||
import kotlinx.io.core.writeText
|
|
||||||
import kotlin.js.Json
|
|
||||||
|
|
||||||
internal actual fun writeJson(meta: Meta, out: Output) {
|
|
||||||
out.writeText(JSON.stringify(meta))
|
|
||||||
}
|
|
||||||
|
|
||||||
internal actual fun readJson(input: Input, length: Int): Meta {
|
|
||||||
val json: Json = JSON.parse(input.readText(max = if (length > 0) length else Int.MAX_VALUE))
|
|
||||||
return JSMeta(json)
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package hep.dataforge.meta
|
|
||||||
|
|
||||||
import hep.dataforge.meta.io.JSMeta
|
|
||||||
import kotlin.js.json
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class JSMetaTest{
|
|
||||||
@Test
|
|
||||||
fun testConverstion(){
|
|
||||||
val test = json(
|
|
||||||
"a" to 2,
|
|
||||||
"b" to "ddd"
|
|
||||||
)
|
|
||||||
val meta = JSMeta(test)
|
|
||||||
assertEquals(2, meta["a"]!!.int)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
package hep.dataforge.meta.io
|
|
||||||
|
|
||||||
import com.github.cliftonlabs.json_simple.JsonArray
|
|
||||||
import com.github.cliftonlabs.json_simple.JsonObject
|
|
||||||
import com.github.cliftonlabs.json_simple.Jsoner
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import hep.dataforge.values.*
|
|
||||||
import kotlinx.io.core.*
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.io.Reader
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.text.ParseException
|
|
||||||
|
|
||||||
internal actual fun writeJson(meta: Meta, out: Output) {
|
|
||||||
val json = meta.toJson()
|
|
||||||
val string = Jsoner.prettyPrint(Jsoner.serialize(json))
|
|
||||||
out.writeText(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Value.toJson(): Any {
|
|
||||||
return if (list.size == 1) {
|
|
||||||
when (type) {
|
|
||||||
ValueType.NUMBER -> number
|
|
||||||
ValueType.BOOLEAN -> boolean
|
|
||||||
else -> string
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
JsonArray().apply {
|
|
||||||
list.forEach { add(it.toJson()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Meta.toJson(): JsonObject {
|
|
||||||
val builder = JsonObject()
|
|
||||||
items.forEach { name, item ->
|
|
||||||
when (item) {
|
|
||||||
is MetaItem.ValueItem -> builder[name.toString()] = item.value.toJson()
|
|
||||||
is MetaItem.NodeItem -> builder[name.toString()] = item.node.toJson()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal actual fun readJson(input: Input, length: Int): Meta {
|
|
||||||
return if (length == 0) {
|
|
||||||
EmptyMeta
|
|
||||||
} else {
|
|
||||||
val json = if (length > 0) {
|
|
||||||
//Read into intermediate buffer
|
|
||||||
val buffer = ByteArray(length)
|
|
||||||
input.readAvailable(buffer, length)
|
|
||||||
Jsoner.deserialize(InputStreamReader(ByteArrayInputStream(buffer), Charsets.UTF_8)) as JsonObject
|
|
||||||
} else {
|
|
||||||
//automatic
|
|
||||||
val reader = object : Reader() {
|
|
||||||
override fun close() {
|
|
||||||
input.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(cbuf: CharArray, off: Int, len: Int): Int {
|
|
||||||
val buffer = ByteBuffer.allocate(len)
|
|
||||||
val res = input.readAvailable(buffer)
|
|
||||||
val chars = String(buffer.array()).toCharArray()
|
|
||||||
System.arraycopy(chars, 0, cbuf, off, chars.size)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Jsoner.deserialize(reader) as JsonObject
|
|
||||||
}
|
|
||||||
json.toMeta()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ParseException::class)
|
|
||||||
private fun JsonObject.toMeta(): Meta {
|
|
||||||
return buildMeta {
|
|
||||||
this@toMeta.forEach { key, value -> appendValue(key as String, value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JsonArray.toListValue(): Value {
|
|
||||||
val list: List<Value> = this.map { value ->
|
|
||||||
when (value) {
|
|
||||||
null -> Null
|
|
||||||
is JsonArray -> value.toListValue()
|
|
||||||
is Number -> NumberValue(value)
|
|
||||||
is Boolean -> if (value) True else False
|
|
||||||
is String -> LazyParsedValue(value)
|
|
||||||
is JsonObject -> error("Object values inside multidimensional arrays are not allowed")
|
|
||||||
else -> error("Unknown token $value in json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Value.of(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MetaBuilder.appendValue(key: String, value: Any?) {
|
|
||||||
when (value) {
|
|
||||||
is JsonObject -> this[key] = value.toMeta()
|
|
||||||
is JsonArray -> {
|
|
||||||
if (value.none { it is JsonObject }) {
|
|
||||||
//If all values are primitives or arrays
|
|
||||||
this[key] = value.toListValue()
|
|
||||||
} else {
|
|
||||||
val list = value.map<Any, Meta> {
|
|
||||||
when (it) {
|
|
||||||
is JsonObject -> it.toMeta()
|
|
||||||
is JsonArray -> it.toListValue().toMeta()
|
|
||||||
else -> Value.of(it).toMeta()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIndexed(key.toName(), list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Number -> this[key] = NumberValue(value)
|
|
||||||
is Boolean -> this[key] = value
|
|
||||||
is String -> this[key] = LazyParsedValue(value)
|
|
||||||
//ignore anything else
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user