Skip to content

Commit 0f35682

Browse files
Fix incorrect behavior while deserializing maps to sealed classes (#2052)
Fixes #2035
1 parent dc9983a commit 0f35682

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ public sealed class Properties(
5353

5454
protected abstract fun encode(value: Any): Value
5555

56+
@Suppress("UNCHECKED_CAST")
57+
final override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
58+
if (serializer is AbstractPolymorphicSerializer<*>) {
59+
val casted = serializer as AbstractPolymorphicSerializer<Any>
60+
val actualSerializer = casted.findPolymorphicSerializer(this, value as Any)
61+
62+
return actualSerializer.serialize(this, value)
63+
}
64+
65+
return serializer.serialize(this, value)
66+
}
67+
5668
override fun encodeTaggedValue(tag: String, value: Any) {
5769
map[tag] = encode(value)
5870
}
@@ -89,6 +101,19 @@ public sealed class Properties(
89101
return structure(descriptor).also { copyTagsTo(it) }
90102
}
91103

104+
final override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
105+
val type = map["type"]?.toString()
106+
107+
if (deserializer is AbstractPolymorphicSerializer<*>) {
108+
val actualSerializer: DeserializationStrategy<out Any> = deserializer.findPolymorphicSerializer(this, type)
109+
110+
@Suppress("UNCHECKED_CAST")
111+
return actualSerializer.deserialize(this) as T
112+
}
113+
114+
return deserializer.deserialize(this)
115+
}
116+
92117
final override fun decodeTaggedValue(tag: String): Value {
93118
return map.getValue(tag)
94119
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package kotlinx.serialization.properties
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
import kotlin.test.Test
6+
import kotlin.test.assertEquals
7+
import kotlin.test.assertIs
8+
9+
class SealedClassSerializationFromPropertiesTest {
10+
@Serializable
11+
sealed class BaseClass {
12+
abstract val firstProperty: Long
13+
abstract val secondProperty: String
14+
}
15+
16+
@SerialName("FIRSTCHILD")
17+
@Serializable
18+
data class FirstChild(override val firstProperty: Long, override val secondProperty: String) : BaseClass()
19+
20+
@SerialName("SECONDCHILD")
21+
@Serializable
22+
data class SecondChild(override val firstProperty: Long, override val secondProperty: String) : BaseClass()
23+
24+
@Test
25+
fun testPropertiesDeserialization() {
26+
val props = mapOf(
27+
"type" to "FIRSTCHILD",
28+
"firstProperty" to 1L,
29+
"secondProperty" to "one"
30+
)
31+
32+
val instance: BaseClass = Properties.decodeFromMap(props)
33+
34+
assertIs<FirstChild>(instance)
35+
assertEquals(instance.firstProperty, 1)
36+
assertEquals(instance.secondProperty, "one")
37+
}
38+
39+
@Test
40+
fun testPropertiesSerialization() {
41+
val instance: BaseClass = FirstChild(
42+
firstProperty = 1L, secondProperty = "one"
43+
)
44+
45+
val instanceProperties = Properties.encodeToMap(instance)
46+
47+
assertEquals(1L, instanceProperties["firstProperty"])
48+
assertEquals("one", instanceProperties["secondProperty"])
49+
}
50+
}

0 commit comments

Comments
 (0)