Meta serialization update

This commit is contained in:
Alexander Nozik 2019-08-02 15:42:45 +03:00
parent c82eda28d8
commit a729d27d1c
5 changed files with 121 additions and 37 deletions

View File

@ -1,5 +1,6 @@
package hep.dataforge.io
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.*
import hep.dataforge.values.*
import kotlinx.io.core.Input
@ -11,7 +12,7 @@ object BinaryMetaFormat : MetaFormat {
override val name: String = "bin"
override val key: Short = 0x4249//BI
override fun Input.readObject(): Meta {
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
return (readMetaItem() as MetaItem.NodeItem).node
}
@ -69,7 +70,7 @@ object BinaryMetaFormat : MetaFormat {
}
}
override fun Output.writeObject(meta: Meta) {
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
writeChar('M')
writeInt(meta.items.size)
meta.items.forEach { (key, item) ->

View File

@ -1,5 +1,8 @@
package hep.dataforge.io
import hep.dataforge.descriptors.ItemDescriptor
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken
@ -10,6 +13,9 @@ import kotlinx.io.core.Output
import kotlinx.io.core.readText
import kotlinx.io.core.writeText
import kotlinx.serialization.json.*
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
object JsonMetaFormat : MetaFormat {
@ -17,12 +23,12 @@ object JsonMetaFormat : MetaFormat {
override val name: String = "json"
override val key: Short = 0x4a53//"JS"
override fun Output.writeObject(obj: Meta) {
val str = obj.toJson().toString()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
val str = meta.toJson().toString()
writeText(str)
}
override fun Input.readObject(): Meta {
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
val str = readText()
val json = Json.plain.parseJson(str)
@ -34,7 +40,7 @@ object JsonMetaFormat : MetaFormat {
}
}
fun Value.toJson(): JsonElement {
fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
return if (isList()) {
JsonArray(list.map { it.toJson() })
} else {
@ -47,22 +53,35 @@ fun Value.toJson(): JsonElement {
}
}
fun Meta.toJson(): JsonObject {
val map = this.items.mapValues { entry ->
when (val value = entry.value) {
is MetaItem.ValueItem -> value.value.toJson()
is MetaItem.NodeItem -> value.node.toJson()
//Use theese methods to customize JSON key mapping
private fun NameToken.toJsonKey(descriptor: ItemDescriptor?) = toString()
private fun NodeDescriptor?.getDescriptor(key: String) = this?.items?.get(key)
fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject {
//TODO search for same name siblings and arrange them into arrays
val map = this.items.entries.associate {(name,item)->
val itemDescriptor = descriptor?.items?.get(name.body)
val key = name.toJsonKey(itemDescriptor)
val value = when (item) {
is MetaItem.ValueItem -> {
item.value.toJson(itemDescriptor as? ValueDescriptor)
}
is MetaItem.NodeItem -> {
item.node.toJson(itemDescriptor as? NodeDescriptor)
}
}
key to value
}
}.mapKeys { it.key.toString() }
return JsonObject(map)
}
fun JsonObject.toMeta() = JsonMeta(this)
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null) = JsonMeta(this, descriptor)
class JsonMeta(val json: JsonObject) : Meta {
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : Meta {
private fun JsonPrimitive.toValue(): Value {
private fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
return when (this) {
JsonNull -> Null
else -> this.content.parseValue() // Optimize number and boolean parsing
@ -70,30 +89,45 @@ class JsonMeta(val json: JsonObject) : Meta {
}
@Suppress("UNCHECKED_CAST")
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement) = when (value) {
is JsonPrimitive -> this[key] = MetaItem.ValueItem(value.toValue()) as MetaItem<JsonMeta>
is JsonObject -> this[key] = MetaItem.NodeItem(value.toMeta())
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
val itemDescriptor = descriptor.getDescriptor(key)
//use name from descriptor in case descriptor name differs from json key
val name = itemDescriptor?.name ?: key
return when (value) {
is JsonPrimitive -> {
this[name] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
}
is JsonObject -> {
this[name] = MetaItem.NodeItem(value.toMeta(itemDescriptor as? NodeDescriptor))
}
is JsonArray -> {
when {
value.all { it is JsonPrimitive } -> {
val listValue = ListValue(
value.map {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue()
(it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
}
)
this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
this[name] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
}
else -> value.forEachIndexed { index, jsonElement ->
when (jsonElement) {
is JsonObject -> this["$key[$index]"] = MetaItem.NodeItem(JsonMeta(jsonElement))
is JsonPrimitive -> this["$key[$index]"] = MetaItem.ValueItem(jsonElement.toValue()) as MetaItem<JsonMeta>
is JsonObject -> {
this["$name[$index]"] =
MetaItem.NodeItem(jsonElement.toMeta(itemDescriptor as? NodeDescriptor))
}
is JsonPrimitive -> {
this["$name[$index]"] =
MetaItem.ValueItem(jsonElement.toValue(itemDescriptor as? ValueDescriptor))
}
is JsonArray -> TODO("Nested arrays not supported")
}
}
}
}
}
}
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
val map = HashMap<String, MetaItem<JsonMeta>>()

View File

@ -1,9 +1,8 @@
package hep.dataforge.io
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Meta
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket
import kotlinx.io.core.toByteArray
import kotlinx.io.core.*
/**
* A format for meta serialization
@ -11,6 +10,15 @@ import kotlinx.io.core.toByteArray
interface MetaFormat : IOFormat<Meta> {
val name: String
val key: Short
override fun Output.writeObject(obj: Meta) {
writeMeta(obj, null)
}
override fun Input.readObject(): Meta = readMeta(null)
fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?)
fun Input.readMeta(descriptor: NodeDescriptor?): Meta
}
fun Meta.toString(format: MetaFormat = JsonMetaFormat): String = buildPacket {

View File

@ -0,0 +1,38 @@
package hep.dataforge.io
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.toConfig
import kotlinx.serialization.Decoder
import kotlinx.serialization.Encoder
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.json.JsonObjectSerializer
/**
* Serialized for meta
*/
object MetaSerializer : KSerializer<Meta> {
override val descriptor: SerialDescriptor = JsonObjectSerializer.descriptor
override fun deserialize(decoder: Decoder): Meta {
//currently just delegates serialization to json serializer
return JsonObjectSerializer.deserialize(decoder).toMeta()
}
override fun serialize(encoder: Encoder, obj: Meta) {
JsonObjectSerializer.serialize(encoder, obj.toJson())
}
}
object ConfigSerializer: KSerializer<Config>{
override val descriptor: SerialDescriptor = JsonObjectSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
return JsonObjectSerializer.deserialize(decoder).toMeta().toConfig()
}
override fun serialize(encoder: Encoder, obj: Config) {
JsonObjectSerializer.serialize(encoder, obj.toJson())
}
}

View File

@ -39,6 +39,9 @@ interface MetaRepr {
* * Same name siblings are supported via elements with the same [Name] but different queries
*/
interface Meta : MetaRepr {
/**
* Top level items of meta tree
*/
val items: Map<NameToken, MetaItem<*>>
override fun toMeta(): Meta = this