From cfce5d8bf4e24fd6a1caf2cef9f62dd689614ce5 Mon Sep 17 00:00:00 2001 From: EdwarDDay <4127904+EdwarDDay@users.noreply.github.com> Date: Tue, 25 Apr 2023 13:50:16 +0200 Subject: [PATCH] Properties Format: Support sealed/polymorphic classes as class properties (#2255) --- .../serialization/properties/Properties.kt | 4 +- ...ledClassSerializationFromPropertiesTest.kt | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt index 4fa55d299b..8760950c3c 100644 --- a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt +++ b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt @@ -58,6 +58,7 @@ public sealed class Properties( if (serializer is AbstractPolymorphicSerializer<*>) { val casted = serializer as AbstractPolymorphicSerializer val actualSerializer = casted.findPolymorphicSerializer(this, value as Any) + encodeTaggedString(nested("type"), actualSerializer.descriptor.serialName) return actualSerializer.serialize(this, value) } @@ -102,9 +103,8 @@ public sealed class Properties( } final override fun decodeSerializableValue(deserializer: DeserializationStrategy): T { - val type = map["type"]?.toString() - if (deserializer is AbstractPolymorphicSerializer<*>) { + val type = map[nested("type")]?.toString() val actualSerializer: DeserializationStrategy = deserializer.findPolymorphicSerializer(this, type) @Suppress("UNCHECKED_CAST") diff --git a/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt b/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt index 52cd03847f..f933232475 100644 --- a/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt +++ b/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt @@ -1,5 +1,6 @@ package kotlinx.serialization.properties +import kotlin.reflect.KProperty1 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlin.test.Test @@ -21,6 +22,9 @@ class SealedClassSerializationFromPropertiesTest { @Serializable data class SecondChild(override val firstProperty: Long, override val secondProperty: String) : BaseClass() + @Serializable + data class CompositeClass(val item: BaseClass) + @Test fun testPropertiesDeserialization() { val props = mapOf( @@ -44,7 +48,71 @@ class SealedClassSerializationFromPropertiesTest { val instanceProperties = Properties.encodeToMap(instance) + assertEquals("FIRSTCHILD", instanceProperties["type"]) assertEquals(1L, instanceProperties["firstProperty"]) assertEquals("one", instanceProperties["secondProperty"]) } + + @Test + fun testWrappedPropertiesDeserialization() { + val props = mapOf( + "0.type" to "FIRSTCHILD", + "0.firstProperty" to 1L, + "0.secondProperty" to "one", + "1.type" to "SECONDCHILD", + "1.firstProperty" to 2L, + "1.secondProperty" to "two" + ) + + val instances: List = Properties.decodeFromMap(props) + + val expected = listOf(FirstChild(1, "one"), SecondChild(2, "two")) + assertEquals(expected, instances) + } + + @Test + fun testWrappedPropertiesSerialization() { + val instances: List = listOf( + FirstChild(firstProperty = 1L, secondProperty = "one"), + SecondChild(firstProperty = 2L, secondProperty = "two") + ) + + val instanceProperties = Properties.encodeToMap(instances) + + assertEquals("FIRSTCHILD", instanceProperties["0.type"]) + assertEquals(1L, instanceProperties["0.firstProperty"]) + assertEquals("one", instanceProperties["0.secondProperty"]) + assertEquals("SECONDCHILD", instanceProperties["1.type"]) + assertEquals(2L, instanceProperties["1.firstProperty"]) + assertEquals("two", instanceProperties["1.secondProperty"]) + } + + @Test + fun testCompositeClassPropertiesDeserialization() { + val props = mapOf( + "item.type" to "SECONDCHILD", + "item.firstProperty" to 7L, + "item.secondProperty" to "nothing" + ) + + val composite: CompositeClass = Properties.decodeFromMap(props) + + assertEquals(CompositeClass(SecondChild(7L, "nothing")), composite) + } + + @Test + fun testCompositeClassPropertiesSerialization() { + val composite = CompositeClass( + item = FirstChild( + firstProperty = 5L, + secondProperty = "something" + ) + ) + + val compositeProperties = Properties.encodeToMap(composite) + + assertEquals("FIRSTCHILD", compositeProperties["item.type"]) + assertEquals(5L, compositeProperties["item.firstProperty"]) + assertEquals("something", compositeProperties["item.secondProperty"]) + } } \ No newline at end of file