Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incorrect behavior while deserializing maps to sealed classes #2052

Merged
merged 7 commits into from Oct 13, 2022
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
@@ -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"])
}
}