Skip to content

Commit 26454fa

Browse files
committed
Create an implementation that allows automatically registering the sealed children of a class for polymorphic serialization of a shared (non-sealed) type.
1 parent 24c6028 commit 26454fa

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public class SealedClassSerializer<T : Any>(
115115
}
116116
}
117117

118-
private val class2Serializer: Map<KClass<out T>, KSerializer<out T>>
118+
internal val class2Serializer: Map<KClass<out T>, KSerializer<out T>>
119119
private val serialName2Serializer: Map<String, KSerializer<out T>>
120120

121121
init {

core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,16 @@ public class PolymorphicModuleBuilder<in Base : Any> @PublishedApi internal cons
2727
* Registers a [subclass] [serializer] in the resulting module under the [base class][Base].
2828
*/
2929
public fun <T : Base> subclass(subclass: KClass<T>, serializer: KSerializer<T>) {
30-
subclasses.add(subclass to serializer)
30+
31+
if (serializer is SealedClassSerializer) {
32+
for ((subsubclass, subserializer) in serializer.class2Serializer.entries) {
33+
@Suppress("UNCHECKED_CAST")
34+
// We don't know the type here, but it matches if correct in the sealed serializer.
35+
subclass(subsubclass as KClass<T>, subserializer as KSerializer<T>)
36+
}
37+
} else {
38+
subclasses.add(subclass to serializer)
39+
}
3140
}
3241

3342
/**

docs/polymorphism.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,11 @@ fun main() {
408408
{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
409409
```
410410

411+
### Registering sealed children as subclasses
412+
A sealed parent interface or class can be directly registered as subclass. This will expose all children that would
413+
be available when serializing the parent directly, but now as sealed. Please note that this is will remain open
414+
serialization, and the sealed parent serializer will not be used in serialization.
415+
411416
<!--- TEST LINES_START -->
412417

413418
### Property of an interface type
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.features
6+
7+
import kotlinx.serialization.*
8+
import kotlinx.serialization.json.Json
9+
import kotlinx.serialization.modules.*
10+
import kotlinx.serialization.test.assertStringFormAndRestored
11+
import kotlin.test.Test
12+
13+
class PolymorphicSealedChildTest {
14+
15+
@Serializable
16+
data class FooHolder(
17+
val someMetadata: Int,
18+
val payload: List<@Polymorphic FooBase>
19+
)
20+
21+
@Serializable
22+
abstract class FooBase
23+
24+
@Serializable
25+
@SerialName("Foo")
26+
sealed class Foo: FooBase() {
27+
@Serializable
28+
@SerialName("Bar")
29+
data class Bar(val bar: Int) : Foo()
30+
@Serializable
31+
@SerialName("Baz")
32+
data class Baz(val baz: String) : Foo()
33+
}
34+
35+
val sealedModule = SerializersModule {
36+
polymorphic(FooBase::class) {
37+
subclass(Foo.serializer())
38+
}
39+
}
40+
41+
val json = Json { serializersModule = sealedModule }
42+
43+
@Test
44+
fun testSaveSealedClassesList() {
45+
assertStringFormAndRestored(
46+
"""{"someMetadata":42,"payload":[
47+
|{"type":"Bar","bar":1},
48+
|{"type":"Baz","baz":"2"}]}""".trimMargin().replace("\n", ""),
49+
FooHolder(42, listOf(Foo.Bar(1), Foo.Baz("2"))),
50+
FooHolder.serializer(),
51+
json,
52+
printResult = true
53+
)
54+
}
55+
56+
@Test
57+
fun testCanSerializeSealedClassPolymorphicallyOnTopLevel() {
58+
assertStringFormAndRestored(
59+
"""{"type":"Bar","bar":1}""",
60+
Foo.Bar(1),
61+
PolymorphicSerializer(FooBase::class),
62+
json
63+
)
64+
}
65+
}

0 commit comments

Comments
 (0)