From 0f35682bbcb41fa3adb24195658b892401f5f582 Mon Sep 17 00:00:00 2001 From: Rodrigo Vedovato Date: Thu, 13 Oct 2022 12:19:03 -0300 Subject: [PATCH] Fix incorrect behavior while deserializing maps to sealed classes (#2052) Fixes #2035 --- .../serialization/properties/Properties.kt | 25 ++++++++++ ...ledClassSerializationFromPropertiesTest.kt | 50 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt diff --git a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt index b65bb3df6..22130f997 100644 --- a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt +++ b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt @@ -53,6 +53,18 @@ public sealed class Properties( protected abstract fun encode(value: Any): Value + @Suppress("UNCHECKED_CAST") + final override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) { + if (serializer is AbstractPolymorphicSerializer<*>) { + val casted = serializer as AbstractPolymorphicSerializer + val actualSerializer = casted.findPolymorphicSerializer(this, value as Any) + + return actualSerializer.serialize(this, value) + } + + return serializer.serialize(this, value) + } + override fun encodeTaggedValue(tag: String, value: Any) { map[tag] = encode(value) } @@ -89,6 +101,19 @@ public sealed class Properties( return structure(descriptor).also { copyTagsTo(it) } } + final override fun decodeSerializableValue(deserializer: DeserializationStrategy): T { + val type = map["type"]?.toString() + + if (deserializer is AbstractPolymorphicSerializer<*>) { + val actualSerializer: DeserializationStrategy = deserializer.findPolymorphicSerializer(this, type) + + @Suppress("UNCHECKED_CAST") + return actualSerializer.deserialize(this) as T + } + + return deserializer.deserialize(this) + } + final override fun decodeTaggedValue(tag: String): Value { return map.getValue(tag) } diff --git a/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt b/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt new file mode 100644 index 000000000..52cd03847 --- /dev/null +++ b/formats/properties/commonTest/src/kotlinx/serialization/properties/SealedClassSerializationFromPropertiesTest.kt @@ -0,0 +1,50 @@ +package kotlinx.serialization.properties + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs + +class SealedClassSerializationFromPropertiesTest { + @Serializable + sealed class BaseClass { + abstract val firstProperty: Long + abstract val secondProperty: String + } + + @SerialName("FIRSTCHILD") + @Serializable + data class FirstChild(override val firstProperty: Long, override val secondProperty: String) : BaseClass() + + @SerialName("SECONDCHILD") + @Serializable + data class SecondChild(override val firstProperty: Long, override val secondProperty: String) : BaseClass() + + @Test + fun testPropertiesDeserialization() { + val props = mapOf( + "type" to "FIRSTCHILD", + "firstProperty" to 1L, + "secondProperty" to "one" + ) + + val instance: BaseClass = Properties.decodeFromMap(props) + + assertIs(instance) + assertEquals(instance.firstProperty, 1) + assertEquals(instance.secondProperty, "one") + } + + @Test + fun testPropertiesSerialization() { + val instance: BaseClass = FirstChild( + firstProperty = 1L, secondProperty = "one" + ) + + val instanceProperties = Properties.encodeToMap(instance) + + assertEquals(1L, instanceProperties["firstProperty"]) + assertEquals("one", instanceProperties["secondProperty"]) + } +} \ No newline at end of file