Move Meta serialization to dataforge-meta
This commit is contained in:
parent
93c806c3bf
commit
e2845f4efc
@ -1,12 +1,12 @@
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
val toolsVersion = "0.3.1"
|
val toolsVersion = "0.4.0"
|
||||||
id("scientifik.mpp") version toolsVersion apply false
|
id("scientifik.mpp") version toolsVersion apply false
|
||||||
id("scientifik.jvm") version toolsVersion apply false
|
id("scientifik.jvm") version toolsVersion apply false
|
||||||
id("scientifik.publish") version toolsVersion apply false
|
id("scientifik.publish") version toolsVersion apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.1.5-dev-9")
|
val dataforgeVersion by extra("0.1.5-dev-10")
|
||||||
|
|
||||||
val bintrayRepo by extra("dataforge")
|
val bintrayRepo by extra("dataforge")
|
||||||
val githubProject by extra("dataforge-core")
|
val githubProject by extra("dataforge-core")
|
||||||
|
@ -12,20 +12,20 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(project(":dataforge-meta"))
|
api(project(":dataforge-meta"))
|
||||||
api(kotlin("reflect"))
|
api(kotlin("reflect"))
|
||||||
api("io.github.microutils:kotlin-logging-common:1.7.2")
|
api("io.github.microutils:kotlin-logging-common:1.7.8")
|
||||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion")
|
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("io.github.microutils:kotlin-logging:1.7.2")
|
api("io.github.microutils:kotlin-logging:1.7.8")
|
||||||
api("ch.qos.logback:logback-classic:1.2.3")
|
api("ch.qos.logback:logback-classic:1.2.3")
|
||||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jsMain by getting {
|
val jsMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("io.github.microutils:kotlin-logging-js:1.7.2")
|
api("io.github.microutils:kotlin-logging-js:1.7.8")
|
||||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion")
|
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ open class Context(
|
|||||||
plugins.forEach { it.detach() }
|
plugins.forEach { it.detach() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"parent" to parent?.name
|
"parent" to parent?.name
|
||||||
"properties" put properties.seal()
|
"properties" put properties.seal()
|
||||||
"plugins" put plugins.map { it.toMeta() }
|
"plugins" put plugins.map { it.toMeta() }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hep.dataforge.context
|
package hep.dataforge.context
|
||||||
|
|
||||||
import hep.dataforge.meta.DFBuilder
|
import hep.dataforge.meta.DFBuilder
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaBuilder
|
import hep.dataforge.meta.MetaBuilder
|
||||||
import hep.dataforge.meta.buildMeta
|
import hep.dataforge.meta.buildMeta
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
@ -22,11 +23,11 @@ class ContextBuilder(var name: String = "@anonymous", val parent: Context = Glob
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) {
|
fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) {
|
||||||
plugins.add(PluginRepository.fetch(tag, buildMeta(action)))
|
plugins.add(PluginRepository.fetch(tag, Meta(action)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) {
|
fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) {
|
||||||
plugins.add(builder.invoke(buildMeta(action)))
|
plugins.add(builder.invoke(Meta(action)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) {
|
fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) {
|
||||||
|
@ -65,7 +65,7 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr {
|
|||||||
*/
|
*/
|
||||||
fun detach()
|
fun detach()
|
||||||
|
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"context" put context.name.toString()
|
"context" put context.name.toString()
|
||||||
"type" to this::class.simpleName
|
"type" to this::class.simpleName
|
||||||
"tag" put tag
|
"tag" put tag
|
||||||
|
@ -102,7 +102,7 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
|
|||||||
load(factory(meta, context))
|
load(factory(meta, context))
|
||||||
|
|
||||||
fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
|
fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
|
||||||
load(factory, buildMeta(metaBuilder))
|
load(factory, Meta(metaBuilder))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a plugin from [PluginManager]
|
* Remove a plugin from [PluginManager]
|
||||||
@ -134,7 +134,7 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
|
|||||||
factory: PluginFactory<T>,
|
factory: PluginFactory<T>,
|
||||||
recursive: Boolean = true,
|
recursive: Boolean = true,
|
||||||
metaBuilder: MetaBuilder.() -> Unit
|
metaBuilder: MetaBuilder.() -> Unit
|
||||||
): T = fetch(factory, recursive, buildMeta(metaBuilder))
|
): T = fetch(factory, recursive, Meta(metaBuilder))
|
||||||
|
|
||||||
override fun iterator(): Iterator<Plugin> = plugins.iterator()
|
override fun iterator(): Iterator<Plugin> = plugins.iterator()
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ data class PluginTag(
|
|||||||
|
|
||||||
override fun toString(): String = listOf(group, name, version).joinToString(separator = ":")
|
override fun toString(): String = listOf(group, name, version).joinToString(separator = ":")
|
||||||
|
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"name" put name
|
"name" put name
|
||||||
"group" put group
|
"group" put group
|
||||||
"version" put version
|
"version" put version
|
||||||
|
@ -19,7 +19,7 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
|
|||||||
*/
|
*/
|
||||||
val meta: Meta
|
val meta: Meta
|
||||||
|
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"type" put (type.simpleName?:"undefined")
|
"type" put (type.simpleName?:"undefined")
|
||||||
if(!meta.isEmpty()) {
|
if(!meta.isEmpty()) {
|
||||||
"meta" put meta
|
"meta" put meta
|
||||||
|
@ -47,7 +47,7 @@ interface DataNode<out T : Any> : MetaRepr {
|
|||||||
|
|
||||||
val meta: Meta
|
val meta: Meta
|
||||||
|
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"type" put (type.simpleName ?: "undefined")
|
"type" put (type.simpleName ?: "undefined")
|
||||||
"items" put {
|
"items" put {
|
||||||
this@DataNode.items.forEach {
|
this@DataNode.items.forEach {
|
||||||
@ -255,11 +255,11 @@ fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, meta: Meta = EmptyM
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) {
|
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) {
|
||||||
this[name] = Data.static(data, buildMeta(block))
|
this[name] = Data.static(data, Meta(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) {
|
fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) {
|
||||||
this[name.toName()] = Data.static(data, buildMeta(block))
|
this[name.toName()] = Data.static(data, Meta(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) {
|
fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) {
|
||||||
|
@ -9,6 +9,8 @@ import hep.dataforge.meta.descriptors.ValueDescriptor
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaBase
|
import hep.dataforge.meta.MetaBase
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.meta.serialization.toJson
|
||||||
|
import hep.dataforge.meta.serialization.toMeta
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.values.*
|
import hep.dataforge.values.*
|
||||||
@ -16,6 +18,7 @@ import kotlinx.io.Input
|
|||||||
import kotlinx.io.Output
|
import kotlinx.io.Output
|
||||||
import kotlinx.io.text.readUtf8String
|
import kotlinx.io.text.readUtf8String
|
||||||
import kotlinx.io.text.writeUtf8String
|
import kotlinx.io.text.writeUtf8String
|
||||||
|
import kotlinx.serialization.UnstableDefault
|
||||||
|
|
||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
@ -23,8 +26,8 @@ import kotlin.collections.component1
|
|||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
@OptIn(UnstableDefault::class)
|
||||||
class JsonMetaFormat(private val json: Json = Json.indented) : MetaFormat {
|
class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat {
|
||||||
|
|
||||||
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
|
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
|
||||||
val jsonObject = meta.toJson(descriptor)
|
val jsonObject = meta.toJson(descriptor)
|
||||||
@ -38,6 +41,8 @@ class JsonMetaFormat(private val json: Json = Json.indented) : MetaFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object : MetaFormatFactory {
|
companion object : MetaFormatFactory {
|
||||||
|
val DEFAULT_JSON = Json{prettyPrint = true}
|
||||||
|
|
||||||
override fun invoke(meta: Meta, context: Context): MetaFormat = default
|
override fun invoke(meta: Meta, context: Context): MetaFormat = default
|
||||||
|
|
||||||
override val shortName = "json"
|
override val shortName = "json"
|
||||||
@ -52,125 +57,3 @@ class JsonMetaFormat(private val json: Json = Json.indented) : MetaFormat {
|
|||||||
default.run { readMeta(descriptor) }
|
default.run { readMeta(descriptor) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param descriptor reserved for custom serialization in future
|
|
||||||
*/
|
|
||||||
fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
|
|
||||||
return if (isList()) {
|
|
||||||
JsonArray(list.map { it.toJson() })
|
|
||||||
} else {
|
|
||||||
when (type) {
|
|
||||||
ValueType.NUMBER -> JsonPrimitive(number)
|
|
||||||
ValueType.STRING -> JsonPrimitive(string)
|
|
||||||
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
|
||||||
ValueType.NULL -> JsonNull
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Use these 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
|
|
||||||
}
|
|
||||||
return JsonObject(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun JsonElement.toMeta(descriptor: NodeDescriptor? = null): Meta {
|
|
||||||
return when (val item = toMetaItem(descriptor)) {
|
|
||||||
is MetaItem.NodeItem<*> -> item.node
|
|
||||||
is MetaItem.ValueItem -> item.value.toMeta()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
|
|
||||||
return when (this) {
|
|
||||||
JsonNull -> Null
|
|
||||||
else -> this.content.parseValue() // Optimize number and boolean parsing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
|
|
||||||
is JsonPrimitive -> {
|
|
||||||
val value = this.toValue(descriptor as? ValueDescriptor)
|
|
||||||
MetaItem.ValueItem(value)
|
|
||||||
}
|
|
||||||
is JsonObject -> {
|
|
||||||
val meta = JsonMeta(this, descriptor as? NodeDescriptor)
|
|
||||||
MetaItem.NodeItem(meta)
|
|
||||||
}
|
|
||||||
is JsonArray -> {
|
|
||||||
if (this.all { it is JsonPrimitive }) {
|
|
||||||
val value = if (isEmpty()) {
|
|
||||||
Null
|
|
||||||
} else {
|
|
||||||
ListValue(
|
|
||||||
map<JsonElement, Value> {
|
|
||||||
//We already checked that all values are primitives
|
|
||||||
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetaItem.ValueItem(value)
|
|
||||||
} else {
|
|
||||||
json {
|
|
||||||
"@value" to this@toMetaItem
|
|
||||||
}.toMetaItem(descriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
|
|
||||||
val itemDescriptor = descriptor?.items?.get(key)
|
|
||||||
return when (value) {
|
|
||||||
is JsonPrimitive -> {
|
|
||||||
this[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
|
|
||||||
}
|
|
||||||
is JsonObject -> {
|
|
||||||
this[key] = MetaItem.NodeItem(JsonMeta(value, 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(itemDescriptor as? ValueDescriptor)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
|
|
||||||
}
|
|
||||||
else -> value.forEachIndexed { index, jsonElement ->
|
|
||||||
this["$key[$index]"] = jsonElement.toMetaItem(itemDescriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
|
|
||||||
val map = HashMap<String, MetaItem<JsonMeta>>()
|
|
||||||
json.forEach { (key, value) -> map[key] = value }
|
|
||||||
map.mapKeys { it.key.toName().first()!! }
|
|
||||||
}
|
|
||||||
}
|
|
@ -119,7 +119,7 @@ class TaglessEnvelopeFormat(
|
|||||||
var line: String
|
var line: String
|
||||||
do {
|
do {
|
||||||
line = readUtf8Line()// ?: error("Input does not contain tagless envelope header")
|
line = readUtf8Line()// ?: error("Input does not contain tagless envelope header")
|
||||||
offset += line.toUtf8Bytes().size.toUInt()
|
offset += line.encodeToByteArray().size.toUInt()
|
||||||
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
|
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
|
||||||
val properties = HashMap<String, String>()
|
val properties = HashMap<String, String>()
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ class TaglessEnvelopeFormat(
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
line = readUtf8Line()
|
line = readUtf8Line()
|
||||||
offset += line.toUtf8Bytes().size.toUInt()
|
offset += line.encodeToByteArray().size.toUInt()
|
||||||
} catch (ex: EOFException) {
|
} catch (ex: EOFException) {
|
||||||
return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
|
return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ class TaglessEnvelopeFormat(
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
line = readUtf8Line() ?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
|
line = readUtf8Line() ?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
|
||||||
offset += line.toUtf8Bytes().size.toUInt()
|
offset += line.encodeToByteArray().size.toUInt()
|
||||||
//returning an Envelope without data if end of input is reached
|
//returning an Envelope without data if end of input is reached
|
||||||
} while (!line.startsWith(dataStart))
|
} while (!line.startsWith(dataStart))
|
||||||
|
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
package hep.dataforge.io.serialization
|
|
||||||
|
|
||||||
import hep.dataforge.io.toJson
|
|
||||||
import hep.dataforge.io.toMeta
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.names.NameToken
|
|
||||||
import hep.dataforge.values.*
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.internal.*
|
|
||||||
import kotlinx.serialization.json.JsonInput
|
|
||||||
import kotlinx.serialization.json.JsonObjectSerializer
|
|
||||||
import kotlinx.serialization.json.JsonOutput
|
|
||||||
|
|
||||||
|
|
||||||
@Serializer(Value::class)
|
|
||||||
@UseExperimental(InternalSerializationApi::class)
|
|
||||||
object ValueSerializer : KSerializer<Value> {
|
|
||||||
private val valueTypeSerializer = EnumSerializer(ValueType::class)
|
|
||||||
private val listSerializer by lazy { ArrayListSerializer(ValueSerializer) }
|
|
||||||
|
|
||||||
override val descriptor: SerialDescriptor = descriptor("Value") {
|
|
||||||
boolean("isList")
|
|
||||||
enum<ValueType>("valueType")
|
|
||||||
element("value", null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Decoder.decodeValue(): Value {
|
|
||||||
return when (decode(valueTypeSerializer)) {
|
|
||||||
ValueType.NULL -> Null
|
|
||||||
ValueType.NUMBER -> decodeDouble().asValue() //TODO differentiate?
|
|
||||||
ValueType.BOOLEAN -> decodeBoolean().asValue()
|
|
||||||
ValueType.STRING -> decodeString().asValue()
|
|
||||||
else -> decodeString().parseValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Value {
|
|
||||||
val isList = decoder.decodeBoolean()
|
|
||||||
return if (isList) {
|
|
||||||
listSerializer.deserialize(decoder).asValue()
|
|
||||||
} else {
|
|
||||||
decoder.decodeValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Encoder.encodeValue(value: Value) {
|
|
||||||
encode(valueTypeSerializer, value.type)
|
|
||||||
when (value.type) {
|
|
||||||
ValueType.NULL -> {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
ValueType.NUMBER -> encodeDouble(value.double)
|
|
||||||
ValueType.BOOLEAN -> encodeBoolean(value.boolean)
|
|
||||||
ValueType.STRING -> encodeString(value.string)
|
|
||||||
else -> encodeString(value.string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: Value) {
|
|
||||||
encoder.encodeBoolean(obj.isList())
|
|
||||||
if (obj.isList()) {
|
|
||||||
listSerializer.serialize(encoder, obj.list)
|
|
||||||
} else {
|
|
||||||
encoder.encodeValue(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializer(MetaItem::class)
|
|
||||||
object MetaItemSerializer : KSerializer<MetaItem<*>> {
|
|
||||||
override val descriptor: SerialDescriptor = descriptor("MetaItem") {
|
|
||||||
boolean("isNode")
|
|
||||||
element("value", null)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MetaItem<*> {
|
|
||||||
val isNode = decoder.decodeBoolean()
|
|
||||||
return if (isNode) {
|
|
||||||
MetaItem.NodeItem(decoder.decode(MetaSerializer))
|
|
||||||
} else {
|
|
||||||
MetaItem.ValueItem(decoder.decode(ValueSerializer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: MetaItem<*>) {
|
|
||||||
encoder.encodeBoolean(obj is MetaItem.NodeItem)
|
|
||||||
when (obj) {
|
|
||||||
is MetaItem.NodeItem -> MetaSerializer.serialize(encoder, obj.node)
|
|
||||||
is MetaItem.ValueItem -> ValueSerializer.serialize(encoder, obj.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DeserializedMeta(override val items: Map<NameToken, MetaItem<*>>) : MetaBase()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialized for meta
|
|
||||||
*/
|
|
||||||
@Serializer(Meta::class)
|
|
||||||
object MetaSerializer : KSerializer<Meta> {
|
|
||||||
private val mapSerializer = HashMapSerializer(
|
|
||||||
StringSerializer,
|
|
||||||
MetaItemSerializer
|
|
||||||
)
|
|
||||||
|
|
||||||
override val descriptor: SerialDescriptor = NamedMapClassDescriptor(
|
|
||||||
"hep.dataforge.meta.Meta",
|
|
||||||
StringSerializer.descriptor,
|
|
||||||
MetaItemSerializer.descriptor
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Meta {
|
|
||||||
return if (decoder is JsonInput) {
|
|
||||||
JsonObjectSerializer.deserialize(decoder).toMeta()
|
|
||||||
} else {
|
|
||||||
DeserializedMeta(mapSerializer.deserialize(decoder).mapKeys { NameToken(it.key) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: Meta) {
|
|
||||||
if (encoder is JsonOutput) {
|
|
||||||
JsonObjectSerializer.serialize(encoder, obj.toJson())
|
|
||||||
} else {
|
|
||||||
mapSerializer.serialize(encoder, obj.items.mapKeys { it.key.toString() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializer(Config::class)
|
|
||||||
object ConfigSerializer : KSerializer<Config> {
|
|
||||||
override val descriptor: SerialDescriptor = MetaSerializer.descriptor
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Config {
|
|
||||||
return MetaSerializer.deserialize(decoder).asConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: Config) {
|
|
||||||
MetaSerializer.serialize(encoder, obj)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package hep.dataforge.io.serialization
|
|
||||||
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.NameToken
|
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.internal.StringDescriptor
|
|
||||||
|
|
||||||
@Serializer(Name::class)
|
|
||||||
object NameSerializer : KSerializer<Name> {
|
|
||||||
override val descriptor: SerialDescriptor = StringDescriptor.withName("Name")
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Name {
|
|
||||||
return decoder.decodeString().toName()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: Name) {
|
|
||||||
encoder.encodeString(obj.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializer(NameToken::class)
|
|
||||||
object NameTokenSerializer : KSerializer<NameToken> {
|
|
||||||
override val descriptor: SerialDescriptor = StringDescriptor.withName("NameToken")
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): NameToken {
|
|
||||||
return decoder.decodeString().toName().first()!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, obj: NameToken) {
|
|
||||||
encoder.encodeString(obj.toString())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package hep.dataforge.io.serialization
|
|
||||||
|
|
||||||
import hep.dataforge.meta.DFExperimental
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.internal.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A convenience builder for serial descriptors
|
|
||||||
*/
|
|
||||||
inline class SerialDescriptorBuilder(private val impl: SerialClassDescImpl) {
|
|
||||||
fun element(
|
|
||||||
name: String,
|
|
||||||
descriptor: SerialDescriptor?,
|
|
||||||
isOptional: Boolean = false,
|
|
||||||
vararg annotations: Annotation
|
|
||||||
) {
|
|
||||||
impl.addElement(name, isOptional)
|
|
||||||
descriptor?.let { impl.pushDescriptor(descriptor) }
|
|
||||||
annotations.forEach {
|
|
||||||
impl.pushAnnotation(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun element(
|
|
||||||
name: String,
|
|
||||||
isOptional: Boolean = false,
|
|
||||||
vararg annotations: Annotation,
|
|
||||||
block: SerialDescriptorBuilder.() -> Unit
|
|
||||||
) {
|
|
||||||
impl.addElement(name, isOptional)
|
|
||||||
impl.pushDescriptor(SerialDescriptorBuilder(SerialClassDescImpl(name)).apply(block).build())
|
|
||||||
annotations.forEach {
|
|
||||||
impl.pushAnnotation(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun boolean(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, BooleanDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun string(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, StringDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun int(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, IntDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun double(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, DoubleDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun float(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, FloatDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun long(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, LongDescriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun doubleArray(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, DoubleArraySerializer.descriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
@UseExperimental(InternalSerializationApi::class)
|
|
||||||
inline fun <reified E : Enum<E>> enum(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
|
||||||
element(name, EnumSerializer(E::class).descriptor, isOptional, *annotations)
|
|
||||||
|
|
||||||
fun classAnnotation(a: Annotation) = impl.pushClassAnnotation(a)
|
|
||||||
|
|
||||||
fun build(): SerialDescriptor = impl
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : Any> KSerializer<T>.descriptor(
|
|
||||||
name: String,
|
|
||||||
block: SerialDescriptorBuilder.() -> Unit
|
|
||||||
): SerialDescriptor =
|
|
||||||
SerialDescriptorBuilder(SerialClassDescImpl(name)).apply(block).build()
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
inline fun <R> Decoder.decodeStructure(
|
|
||||||
desc: SerialDescriptor,
|
|
||||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
|
||||||
crossinline block: CompositeDecoder.() -> R
|
|
||||||
): R {
|
|
||||||
val decoder = beginStructure(desc, *typeParams)
|
|
||||||
val res = decoder.block()
|
|
||||||
decoder.endStructure(desc)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun Encoder.encodeStructure(
|
|
||||||
desc: SerialDescriptor,
|
|
||||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
|
||||||
block: CompositeEncoder.() -> Unit
|
|
||||||
) {
|
|
||||||
val encoder = beginStructure(desc, *typeParams)
|
|
||||||
encoder.block()
|
|
||||||
encoder.endStructure(desc)
|
|
||||||
}
|
|
@ -1,5 +1,9 @@
|
|||||||
|
import scientifik.useSerialization
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("scientifik.mpp")
|
id("scientifik.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useSerialization()
|
||||||
|
|
||||||
description = "Meta definition and basic operations on meta"
|
description = "Meta definition and basic operations on meta"
|
@ -64,7 +64,7 @@ class Laminate(layers: List<Meta>) : MetaBase() {
|
|||||||
}
|
}
|
||||||
else -> map {
|
else -> map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is MetaItem.ValueItem -> MetaItem.NodeItem(buildMeta { Meta.VALUE_KEY put it.value })
|
is MetaItem.ValueItem -> MetaItem.NodeItem(Meta { Meta.VALUE_KEY put it.value })
|
||||||
is MetaItem.NodeItem -> it
|
is MetaItem.NodeItem -> it
|
||||||
}
|
}
|
||||||
}.merge()
|
}.merge()
|
||||||
|
@ -3,11 +3,13 @@ package hep.dataforge.meta
|
|||||||
import hep.dataforge.meta.Meta.Companion.VALUE_KEY
|
import hep.dataforge.meta.Meta.Companion.VALUE_KEY
|
||||||
import hep.dataforge.meta.MetaItem.NodeItem
|
import hep.dataforge.meta.MetaItem.NodeItem
|
||||||
import hep.dataforge.meta.MetaItem.ValueItem
|
import hep.dataforge.meta.MetaItem.ValueItem
|
||||||
|
import hep.dataforge.meta.serialization.toJson
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
import hep.dataforge.values.EnumValue
|
import hep.dataforge.values.EnumValue
|
||||||
import hep.dataforge.values.Null
|
import hep.dataforge.values.Null
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import hep.dataforge.values.boolean
|
import hep.dataforge.values.boolean
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,11 +17,14 @@ import hep.dataforge.values.boolean
|
|||||||
* * a [ValueItem] (leaf)
|
* * a [ValueItem] (leaf)
|
||||||
* * a [NodeItem] (node)
|
* * a [NodeItem] (node)
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
sealed class MetaItem<out M : Meta> {
|
sealed class MetaItem<out M : Meta> {
|
||||||
|
@Serializable
|
||||||
data class ValueItem(val value: Value) : MetaItem<Nothing>() {
|
data class ValueItem(val value: Value) : MetaItem<Nothing>() {
|
||||||
override fun toString(): String = value.toString()
|
override fun toString(): String = value.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class NodeItem<M : Meta>(val node: M) : MetaItem<M>() {
|
data class NodeItem<M : Meta>(val node: M) : MetaItem<M>() {
|
||||||
override fun toString(): String = node.toString()
|
override fun toString(): String = node.toString()
|
||||||
}
|
}
|
||||||
@ -161,7 +166,7 @@ abstract class MetaBase : Meta {
|
|||||||
|
|
||||||
override fun hashCode(): Int = items.hashCode()
|
override fun hashCode(): Int = items.hashCode()
|
||||||
|
|
||||||
override fun toString(): String = items.toString()
|
override fun toString(): String = toJson().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,8 +153,3 @@ fun buildMeta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().appl
|
|||||||
*/
|
*/
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder)
|
fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder)
|
||||||
|
|
||||||
/**
|
|
||||||
* Build meta using given source meta as a base
|
|
||||||
*/
|
|
||||||
fun buildMeta(source: Meta, builder: MetaBuilder.() -> Unit): MetaBuilder = source.builder().apply(builder)
|
|
@ -6,6 +6,6 @@ package hep.dataforge.meta
|
|||||||
@DslMarker
|
@DslMarker
|
||||||
annotation class DFBuilder
|
annotation class DFBuilder
|
||||||
|
|
||||||
@Experimental(level = Experimental.Level.WARNING)
|
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
annotation class DFExperimental
|
annotation class DFExperimental
|
@ -255,10 +255,10 @@ class ValueDescriptor : ItemDescriptor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object : SchemeSpec<ValueDescriptor>(::ValueDescriptor) {
|
companion object : SchemeSpec<ValueDescriptor>(::ValueDescriptor) {
|
||||||
inline fun <reified E : Enum<E>> enum(name: String) = ValueDescriptor {
|
// inline fun <reified E : Enum<E>> enum(name: String) = ValueDescriptor {
|
||||||
type(ValueType.STRING)
|
// type(ValueType.STRING)
|
||||||
this.allowedValues = enumValues<E>().map { Value.of(it.name) }
|
// this.allowedValues = enumValues<E>().map { Value.of(it.name) }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Build a value descriptor from annotation
|
// * Build a value descriptor from annotation
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
package hep.dataforge.meta.serialization
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaBase
|
||||||
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.meta.descriptors.ItemDescriptor
|
||||||
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.values.*
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param descriptor reserved for custom serialization in future
|
||||||
|
*/
|
||||||
|
fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
|
||||||
|
return if (isList()) {
|
||||||
|
JsonArray(list.map { it.toJson() })
|
||||||
|
} else {
|
||||||
|
when (type) {
|
||||||
|
ValueType.NUMBER -> JsonPrimitive(number)
|
||||||
|
ValueType.STRING -> JsonPrimitive(string)
|
||||||
|
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
||||||
|
ValueType.NULL -> JsonNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Use these 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
|
||||||
|
}
|
||||||
|
return JsonObject(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonElement.toMeta(descriptor: NodeDescriptor? = null): Meta {
|
||||||
|
return when (val item = toMetaItem(descriptor)) {
|
||||||
|
is MetaItem.NodeItem<*> -> item.node
|
||||||
|
is MetaItem.ValueItem -> item.value.toMeta()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
|
||||||
|
return when (this) {
|
||||||
|
JsonNull -> Null
|
||||||
|
else -> this.content.parseValue() // Optimize number and boolean parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
|
||||||
|
is JsonPrimitive -> {
|
||||||
|
val value = this.toValue(descriptor as? ValueDescriptor)
|
||||||
|
MetaItem.ValueItem(value)
|
||||||
|
}
|
||||||
|
is JsonObject -> {
|
||||||
|
val meta = JsonMeta(this, descriptor as? NodeDescriptor)
|
||||||
|
MetaItem.NodeItem(meta)
|
||||||
|
}
|
||||||
|
is JsonArray -> {
|
||||||
|
if (this.all { it is JsonPrimitive }) {
|
||||||
|
val value = if (isEmpty()) {
|
||||||
|
Null
|
||||||
|
} else {
|
||||||
|
ListValue(
|
||||||
|
map<JsonElement, Value> {
|
||||||
|
//We already checked that all values are primitives
|
||||||
|
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetaItem.ValueItem(value)
|
||||||
|
} else {
|
||||||
|
json {
|
||||||
|
"@value" to this@toMetaItem
|
||||||
|
}.toMetaItem(descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
|
||||||
|
val itemDescriptor = descriptor?.items?.get(key)
|
||||||
|
return when (value) {
|
||||||
|
is JsonPrimitive -> {
|
||||||
|
this[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
|
||||||
|
}
|
||||||
|
is JsonObject -> {
|
||||||
|
this[key] = MetaItem.NodeItem(JsonMeta(value, 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(itemDescriptor as? ValueDescriptor)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
|
||||||
|
}
|
||||||
|
else -> value.forEachIndexed { index, jsonElement ->
|
||||||
|
this["$key[$index]"] = jsonElement.toMetaItem(itemDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
|
||||||
|
val map = HashMap<String, MetaItem<JsonMeta>>()
|
||||||
|
json.forEach { (key, value) -> map[key] = value }
|
||||||
|
map.mapKeys { it.key.toName().first()!! }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package hep.dataforge.meta.serialization
|
||||||
|
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.values.*
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.builtins.MapSerializer
|
||||||
|
import kotlinx.serialization.builtins.list
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.json.JsonInput
|
||||||
|
import kotlinx.serialization.json.JsonObjectSerializer
|
||||||
|
import kotlinx.serialization.json.JsonOutput
|
||||||
|
|
||||||
|
|
||||||
|
@Serializer(Value::class)
|
||||||
|
@OptIn(InternalSerializationApi::class)
|
||||||
|
object ValueSerializer : KSerializer<Value> {
|
||||||
|
// private val valueTypeSerializer = EnumSerializer(ValueType::class)
|
||||||
|
private val listSerializer by lazy { ValueSerializer.list }
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = SerialDescriptor("Value") {
|
||||||
|
boolean("isList")
|
||||||
|
enum<ValueType>("valueType")
|
||||||
|
string("value")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Decoder.decodeValue(): Value {
|
||||||
|
return when (decode(ValueType.serializer())) {
|
||||||
|
ValueType.NULL -> Null
|
||||||
|
ValueType.NUMBER -> decodeDouble().asValue() //TODO differentiate?
|
||||||
|
ValueType.BOOLEAN -> decodeBoolean().asValue()
|
||||||
|
ValueType.STRING -> decodeString().asValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Value {
|
||||||
|
val isList = decoder.decodeBoolean()
|
||||||
|
return if (isList) {
|
||||||
|
listSerializer.deserialize(decoder).asValue()
|
||||||
|
} else {
|
||||||
|
decoder.decodeValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Encoder.encodeValue(value: Value) {
|
||||||
|
encode(ValueType.serializer(), value.type)
|
||||||
|
when (value.type) {
|
||||||
|
ValueType.NULL -> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
ValueType.NUMBER -> encodeDouble(value.double)
|
||||||
|
ValueType.BOOLEAN -> encodeBoolean(value.boolean)
|
||||||
|
ValueType.STRING -> encodeString(value.string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Value) {
|
||||||
|
encoder.encodeBoolean(value.isList())
|
||||||
|
if (value.isList()) {
|
||||||
|
listSerializer.serialize(encoder, value.list)
|
||||||
|
} else {
|
||||||
|
encoder.encodeValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeserializedMeta(override val items: Map<NameToken, MetaItem<*>>) : MetaBase()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized for meta
|
||||||
|
*/
|
||||||
|
@Serializer(Meta::class)
|
||||||
|
object MetaSerializer : KSerializer<Meta> {
|
||||||
|
private val mapSerializer = MapSerializer(
|
||||||
|
String.serializer(),
|
||||||
|
MetaItem.serializer(MetaSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Meta {
|
||||||
|
return if (decoder is JsonInput) {
|
||||||
|
JsonObjectSerializer.deserialize(decoder).toMeta()
|
||||||
|
} else {
|
||||||
|
DeserializedMeta(mapSerializer.deserialize(decoder).mapKeys { NameToken(it.key) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Meta) {
|
||||||
|
if (encoder is JsonOutput) {
|
||||||
|
JsonObjectSerializer.serialize(encoder, value.toJson())
|
||||||
|
} else {
|
||||||
|
mapSerializer.serialize(encoder, value.items.mapKeys { it.key.toString() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializer(Config::class)
|
||||||
|
object ConfigSerializer : KSerializer<Config> {
|
||||||
|
override val descriptor: SerialDescriptor = MetaSerializer.descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Config {
|
||||||
|
return MetaSerializer.deserialize(decoder).asConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Config) {
|
||||||
|
MetaSerializer.serialize(encoder, value)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package hep.dataforge.meta.serialization
|
||||||
|
|
||||||
|
import hep.dataforge.meta.DFExperimental
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.builtins.DoubleArraySerializer
|
||||||
|
import kotlinx.serialization.internal.*
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.boolean(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, PrimitiveDescriptor(name, PrimitiveKind.BOOLEAN), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.string(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, PrimitiveDescriptor(name, PrimitiveKind.STRING), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.int(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, PrimitiveDescriptor(name, PrimitiveKind.INT), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.double(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name,PrimitiveDescriptor(name, PrimitiveKind.DOUBLE), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.float(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, PrimitiveDescriptor(name, PrimitiveKind.FLOAT), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.long(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, PrimitiveDescriptor(name, PrimitiveKind.LONG), isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
fun SerialDescriptorBuilder.doubleArray(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||||
|
element(name, DoubleArraySerializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
|
||||||
|
@OptIn(InternalSerializationApi::class)
|
||||||
|
inline fun <reified E : Enum<E>> SerialDescriptorBuilder.enum(name: String, isOptional: Boolean = false, vararg annotations: Annotation) {
|
||||||
|
val enumDescriptor = SerialDescriptor(serialName, UnionKind.ENUM_KIND) {
|
||||||
|
enumValues<E>().forEach {
|
||||||
|
val fqn = "$serialName.${it.name}"
|
||||||
|
val enumMemberDescriptor = SerialDescriptor(fqn, StructureKind.OBJECT)
|
||||||
|
element(it.name, enumMemberDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element(name, enumDescriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
//inline fun <reified T : Any> KSerializer<T>.descriptor(
|
||||||
|
// name: String,
|
||||||
|
// block: SerialDescriptorBuilder.() -> Unit
|
||||||
|
//): SerialDescriptor =
|
||||||
|
// SerialDescriptorBuilder(SerialClassDescImpl(name)).apply(block).build()
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
inline fun <R> Decoder.decodeStructure(
|
||||||
|
desc: SerialDescriptor,
|
||||||
|
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||||
|
crossinline block: CompositeDecoder.() -> R
|
||||||
|
): R {
|
||||||
|
val decoder = beginStructure(desc, *typeParams)
|
||||||
|
val res = decoder.block()
|
||||||
|
decoder.endStructure(desc)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Encoder.encodeStructure(
|
||||||
|
desc: SerialDescriptor,
|
||||||
|
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||||
|
block: CompositeEncoder.() -> Unit
|
||||||
|
) {
|
||||||
|
val encoder = beginStructure(desc, *typeParams)
|
||||||
|
encoder.block()
|
||||||
|
encoder.endStructure(desc)
|
||||||
|
}
|
@ -90,7 +90,7 @@ inline class MetaTransformation(val transformations: Collection<TransformationRu
|
|||||||
* Produce new meta using only those items that match transformation rules
|
* Produce new meta using only those items that match transformation rules
|
||||||
*/
|
*/
|
||||||
fun transform(source: Meta): Meta =
|
fun transform(source: Meta): Meta =
|
||||||
buildMeta {
|
Meta {
|
||||||
transformations.forEach { rule ->
|
transformations.forEach { rule ->
|
||||||
rule.selectItems(source).forEach { name ->
|
rule.selectItems(source).forEach { name ->
|
||||||
rule.transformItem(name, source[name], this)
|
rule.transformItem(name, source[name], this)
|
||||||
@ -102,7 +102,7 @@ inline class MetaTransformation(val transformations: Collection<TransformationRu
|
|||||||
* Transform a meta, replacing all elements found in rules with transformed entries
|
* Transform a meta, replacing all elements found in rules with transformed entries
|
||||||
*/
|
*/
|
||||||
fun apply(source: Meta): Meta =
|
fun apply(source: Meta): Meta =
|
||||||
buildMeta(source) {
|
source.edit {
|
||||||
transformations.forEach { rule ->
|
transformations.forEach { rule ->
|
||||||
rule.selectItems(source).forEach { name ->
|
rule.selectItems(source).forEach { name ->
|
||||||
remove(name)
|
remove(name)
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package hep.dataforge.names
|
package hep.dataforge.names
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general interface for working with names.
|
* The general interface for working with names.
|
||||||
* The name is a dot separated list of strings like `token1.token2.token3`.
|
* The name is a dot separated list of strings like `token1.token2.token3`.
|
||||||
* Each token could contain additional index in square brackets.
|
* Each token could contain additional index in square brackets.
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
class Name(val tokens: List<NameToken>) {
|
class Name(val tokens: List<NameToken>) {
|
||||||
|
|
||||||
val length get() = tokens.size
|
val length get() = tokens.size
|
||||||
@ -51,10 +54,21 @@ class Name(val tokens: List<NameToken>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
@Serializer(Name::class)
|
||||||
|
companion object: KSerializer<Name> {
|
||||||
const val NAME_SEPARATOR = "."
|
const val NAME_SEPARATOR = "."
|
||||||
|
|
||||||
val EMPTY = Name(emptyList())
|
val EMPTY = Name(emptyList())
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveDescriptor("Name", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Name {
|
||||||
|
return decoder.decodeString().toName()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Name) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +77,7 @@ class Name(val tokens: List<NameToken>) {
|
|||||||
* Following symbols are prohibited in name tokens: `{}.:\`.
|
* Following symbols are prohibited in name tokens: `{}.:\`.
|
||||||
* A name token could have appendix in square brackets called *index*
|
* A name token could have appendix in square brackets called *index*
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
data class NameToken(val body: String, val index: String = "") {
|
data class NameToken(val body: String, val index: String = "") {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -82,6 +97,19 @@ data class NameToken(val body: String, val index: String = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasIndex() = index.isNotEmpty()
|
fun hasIndex() = index.isNotEmpty()
|
||||||
|
|
||||||
|
@Serializer(NameToken::class)
|
||||||
|
companion object :KSerializer<NameToken>{
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveDescriptor("NameToken", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): NameToken {
|
||||||
|
return decoder.decodeString().toName().first()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: NameToken) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package hep.dataforge.values
|
package hep.dataforge.values
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of supported Value types.
|
* The list of supported Value types.
|
||||||
@ -7,6 +9,7 @@ package hep.dataforge.values
|
|||||||
* Time value and binary value are represented by string
|
* Time value and binary value are represented by string
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
enum class ValueType {
|
enum class ValueType {
|
||||||
NUMBER, STRING, BOOLEAN, NULL
|
NUMBER, STRING, BOOLEAN, NULL
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user