From acfb070938910636f09aeb41daafd4a73673adf6 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 16 Sep 2020 15:10:43 +0300 Subject: [PATCH] New Meta serializer --- .../hep/dataforge/io/MetaSerializerTest.kt | 3 +- .../kotlin/hep/dataforge/meta/Config.kt | 4 +- .../kotlin/hep/dataforge/meta/Meta.kt | 46 ++++------------- .../hep/dataforge/meta/MetaSerializer.kt | 49 ++++++++++++++++--- .../kotlin/hep/dataforge/values/Value.kt | 1 - 5 files changed, 56 insertions(+), 47 deletions(-) diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt index 11177e71..23fef5d5 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt @@ -27,6 +27,7 @@ class MetaSerializerTest { @Test fun testMetaSerialization() { val string = JSON_PRETTY.encodeToString(MetaSerializer, meta) + println(string) val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string) assertEquals(meta, restored) } @@ -35,7 +36,7 @@ class MetaSerializerTest { @Test fun testCborSerialization() { val bytes = Cbor.encodeToByteArray(MetaSerializer, meta) - println(bytes.contentToString()) + println(bytes.decodeToString()) val restored = Cbor.decodeFromByteArray(MetaSerializer, bytes) assertEquals(meta, restored) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 89d944a7..d1691e4e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -28,8 +28,8 @@ public interface ObservableMeta : Meta { /** * Mutable meta representing object state */ -@Serializable -public class Config : AbstractMutableMeta(), ObservableMeta { +@Serializable(Config.Companion::class) +public class Config() : AbstractMutableMeta(), ObservableMeta { private val listeners = HashSet() diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 138ff099..5c992c6b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -4,14 +4,11 @@ import hep.dataforge.meta.Meta.Companion.VALUE_KEY import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* -import hep.dataforge.values.* -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer +import hep.dataforge.values.EnumValue +import hep.dataforge.values.Null +import hep.dataforge.values.Value +import hep.dataforge.values.boolean import kotlinx.serialization.Serializable -import kotlinx.serialization.Serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json @@ -20,29 +17,17 @@ import kotlinx.serialization.json.Json * * a [ValueItem] (leaf) * * a [NodeItem] (node) */ -@Serializable -public sealed class MetaItem { +@Serializable(MetaItemSerializer::class) +public sealed class MetaItem() { abstract override fun equals(other: Any?): Boolean abstract override fun hashCode(): Int - @Serializable + @Serializable(MetaItemSerializer::class) public class ValueItem(public val value: Value) : MetaItem() { override fun toString(): String = value.toString() - @OptIn(ExperimentalSerializationApi::class) - @Serializer(ValueItem::class) - public companion object : KSerializer { - override val descriptor: SerialDescriptor get() = ValueSerializer.descriptor - - override fun deserialize(decoder: Decoder): ValueItem = ValueItem(ValueSerializer.deserialize(decoder)) - - override fun serialize(encoder: Encoder, value: ValueItem) { - ValueSerializer.serialize(encoder, value.value) - } - } - override fun equals(other: Any?): Boolean { return this.value == (other as? ValueItem)?.value } @@ -52,23 +37,11 @@ public sealed class MetaItem { } } - @Serializable - public class NodeItem(@Serializable(MetaSerializer::class) public val node: M) : MetaItem() { + @Serializable(MetaItemSerializer::class) + public class NodeItem(public val node: M) : MetaItem() { //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializeable override fun toString(): String = node.toString() - @OptIn(ExperimentalSerializationApi::class) - @Serializer(NodeItem::class) - public companion object : KSerializer> { - override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor - - override fun deserialize(decoder: Decoder): NodeItem<*> = NodeItem(MetaSerializer.deserialize(decoder)) - - override fun serialize(encoder: Encoder, value: NodeItem<*>) { - MetaSerializer.serialize(encoder, value.node) - } - } - override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node override fun hashCode(): Int = node.hashCode() @@ -112,7 +85,6 @@ public fun interface ItemProvider { * * * Same name siblings are supported via elements with the same [Name] but different queries */ -@Serializable(MetaSerializer::class) public interface Meta : MetaRepr, ItemProvider { /** * Top level items of meta tree diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index dba129bc..7184ae3a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -1,17 +1,54 @@ package hep.dataforge.meta import hep.dataforge.names.NameToken +import hep.dataforge.values.ValueSerializer import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonEncoder import kotlinx.serialization.json.JsonObject +@OptIn(ExperimentalSerializationApi::class) +public object MetaItemSerializer : KSerializer> { + + @OptIn(InternalSerializationApi::class) + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.meta.MetaItem") { + element("isNode") + element("content", buildSerialDescriptor("MetaItem.content", PolymorphicKind.SEALED)) + } + + override fun deserialize(decoder: Decoder): MetaItem<*> { + decoder.decodeStructure(descriptor) { + //Force strict serialization order + require(decodeElementIndex(descriptor) == 0) { "Node flag must be first item serialized" } + val isNode = decodeBooleanElement(descriptor, 0) + require(decodeElementIndex(descriptor) == 1) { "Missing MetaItem content" } + val item = if (isNode) { + decodeSerializableElement(descriptor,1, MetaSerializer).asMetaItem() + } else { + decodeSerializableElement(descriptor,1,ValueSerializer).asMetaItem() + } + require(decodeElementIndex(descriptor) == CompositeDecoder.DECODE_DONE){"Serialized MetaItem contains additional fields"} + return item + } + } + + override fun serialize(encoder: Encoder, value: MetaItem<*>) { + encoder.encodeStructure(descriptor) { + encodeBooleanElement(descriptor, 0, value is MetaItem.NodeItem) + when (value) { + is MetaItem.ValueItem -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value) + is MetaItem.NodeItem -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node) + } + } + } +} + /** * Serialized for meta */ @@ -19,9 +56,9 @@ import kotlinx.serialization.json.JsonObject @Serializer(Meta::class) public object MetaSerializer : KSerializer { - private val mapSerializer = MapSerializer( - NameToken.serializer(), - MetaItem.serializer(MetaSerializer) + private val mapSerializer: KSerializer>> = MapSerializer( + NameToken, + MetaItemSerializer//MetaItem.serializer(MetaSerializer) ) override val descriptor: SerialDescriptor get() = mapSerializer.descriptor diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index ca78f0ae..9136d525 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -19,7 +19,6 @@ public enum class ValueType { * * Value can represent a list of value objects. */ -@Serializable(ValueSerializer::class) public interface Value { /** * Get raw value of this value