Skip to content

Commit b275a2d

Browse files
authored
Contextual serialization for derived classes (#1277)
Fixes #1276 The issue #1276 is happening because, in the implementation of ContextualSerializer, the serializer is looked up using the concrete class instead of the serializable class
1 parent ac54504 commit b275a2d

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

core/commonMain/src/kotlinx/serialization/ContextualSerializer.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,20 @@ public class ContextualSerializer<T : Any>(
4545
private val typeParametersSerializers: Array<KSerializer<*>>
4646
) : KSerializer<T> {
4747

48+
private fun serializer(serializersModule: SerializersModule): KSerializer<T> =
49+
serializersModule.getContextual(serializableClass) ?: fallbackSerializer ?: serializableClass.serializerNotRegistered()
50+
4851
// Used from auto-generated code
4952
public constructor(serializableClass: KClass<T>) : this(serializableClass, null, EMPTY_SERIALIZER_ARRAY)
5053

5154
public override val descriptor: SerialDescriptor =
5255
buildSerialDescriptor("kotlinx.serialization.ContextualSerializer", SerialKind.CONTEXTUAL).withContext(serializableClass)
5356

5457
public override fun serialize(encoder: Encoder, value: T) {
55-
val clz = value::class
56-
val serializer = encoder.serializersModule.getContextual(clz) ?: fallbackSerializer ?: serializableClass.serializerNotRegistered()
57-
@Suppress("UNCHECKED_CAST")
58-
encoder.encodeSerializableValue(serializer as SerializationStrategy<T>, value)
58+
encoder.encodeSerializableValue(serializer(encoder.serializersModule), value)
5959
}
6060

6161
public override fun deserialize(decoder: Decoder): T {
62-
val serializer = decoder.serializersModule.getContextual(serializableClass) ?: fallbackSerializer ?: serializableClass.serializerNotRegistered()
63-
return decoder.decodeSerializableValue(serializer)
62+
return decoder.decodeSerializableValue(serializer(decoder.serializersModule))
6463
}
6564
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package kotlinx.serialization.features
2+
3+
import kotlin.test.*
4+
import kotlinx.serialization.*
5+
import kotlinx.serialization.descriptors.*
6+
import kotlinx.serialization.encoding.*
7+
import kotlinx.serialization.json.*
8+
import kotlinx.serialization.modules.*
9+
10+
class DerivedContextualSerializerTest {
11+
12+
@Serializable
13+
abstract class Message
14+
15+
@Serializable
16+
class SimpleMessage(val body: String) : Message()
17+
18+
@Serializable
19+
class Holder(@Contextual val message: Message)
20+
21+
object MessageAsStringSerializer : KSerializer<Message> {
22+
override val descriptor: SerialDescriptor =
23+
PrimitiveSerialDescriptor("kotlinx.serialization.MessageAsStringSerializer", PrimitiveKind.STRING)
24+
25+
override fun serialize(encoder: Encoder, value: Message) {
26+
// dummy serializer that assumes Message is always SimpleMessage
27+
check(value is SimpleMessage)
28+
encoder.encodeString(value.body)
29+
}
30+
31+
override fun deserialize(decoder: Decoder): Message {
32+
return SimpleMessage(decoder.decodeString())
33+
}
34+
}
35+
36+
@Test
37+
fun testDerivedContextualSerializer() {
38+
val module = SerializersModule {
39+
contextual(MessageAsStringSerializer)
40+
}
41+
val format = Json { serializersModule = module }
42+
val data = Holder(SimpleMessage("hello"))
43+
val serialized = format.encodeToString(data)
44+
assertEquals("""{"message":"hello"}""", serialized)
45+
val deserialized = format.decodeFromString<Holder>(serialized)
46+
assertTrue(deserialized.message is SimpleMessage)
47+
assertEquals("hello", deserialized.message.body)
48+
}
49+
}

0 commit comments

Comments
 (0)