diff options
author | Rodrigo Vedovato <vedovato.rodrigo@gmail.com> | 2022-10-13 12:19:03 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-13 18:19:03 +0300 |
commit | 0f35682bbcb41fa3adb24195658b892401f5f582 (patch) | |
tree | 518089183adc71fba359f81357f48ba5de364dd7 | |
parent | dc9983a608cac6215d2a3ebb8e41e8f710cab126 (diff) | |
download | kotlinx.serialization-0f35682bbcb41fa3adb24195658b892401f5f582.tar.gz |
Fix incorrect behavior while deserializing maps to sealed classes (#2052)
Fixes #2035
2 files changed, 75 insertions, 0 deletions
diff --git a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt index b65bb3df..22130f99 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 <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) { + if (serializer is AbstractPolymorphicSerializer<*>) { + val casted = serializer as AbstractPolymorphicSerializer<Any> + 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 <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T { + val type = map["type"]?.toString() + + if (deserializer is AbstractPolymorphicSerializer<*>) { + val actualSerializer: DeserializationStrategy<out Any> = 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 00000000..52cd0384 --- /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<FirstChild>(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 |