Skip to content

Commit ccf9c2c

Browse files
authored
Correctly handle situation where different serializers can be provided (#2113)
for the same KClass in SealedClassSerializer. Previous fix supported only equal serializers. Fixes #2110 Fixes #1937
1 parent 02f643c commit ccf9c2c

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ public class SealedClassSerializer<T : Any>(
105105
element("type", String.serializer().descriptor)
106106
val elementDescriptor =
107107
buildSerialDescriptor("kotlinx.serialization.Sealed<${baseClass.simpleName}>", SerialKind.CONTEXTUAL) {
108-
subclassSerializers.distinct().forEach {
109-
val d = it.descriptor
110-
element(d.serialName, d)
108+
// serialName2Serializer is guaranteed to have no duplicates — checked in `init`.
109+
serialName2Serializer.forEach { (name, serializer) ->
110+
element(name, serializer.descriptor)
111111
}
112112
}
113113
element("value", elementDescriptor)
@@ -123,6 +123,9 @@ public class SealedClassSerializer<T : Any>(
123123
throw IllegalArgumentException("All subclasses of sealed class ${baseClass.simpleName} should be marked @Serializable")
124124
}
125125

126+
// Note: we do not check whether different serializers are provided if the same KClass duplicated in the `subclasses`.
127+
// Plugin should produce identical serializers, although they are not always strictly equal (e.g. new ObjectSerializer
128+
// may be created every time)
126129
class2Serializer = subclasses.zip(subclassSerializers).toMap()
127130
serialName2Serializer = class2Serializer.entries.groupingBy { it.value.descriptor.serialName }
128131
.aggregate<Map.Entry<KClass<out T>, KSerializer<out T>>, String, Map.Entry<KClass<*>, KSerializer<out T>>>
@@ -137,7 +140,10 @@ public class SealedClassSerializer<T : Any>(
137140
}.mapValues { it.value.value }
138141
}
139142

140-
override fun findPolymorphicSerializerOrNull(decoder: CompositeDecoder, klassName: String?): DeserializationStrategy<T>? {
143+
override fun findPolymorphicSerializerOrNull(
144+
decoder: CompositeDecoder,
145+
klassName: String?
146+
): DeserializationStrategy<T>? {
141147
return serialName2Serializer[klassName] ?: super.findPolymorphicSerializerOrNull(decoder, klassName)
142148
}
143149

core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public class ClassSerialDescriptorBuilder internal constructor(
279279
annotations: List<Annotation> = emptyList(),
280280
isOptional: Boolean = false
281281
) {
282-
require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered" }
282+
require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered in $serialName" }
283283
elementNames += elementName
284284
elementDescriptors += descriptor
285285
elementAnnotations += annotations

formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedDiamondTest.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@ class SealedDiamondTest : JsonTestBase() {
2020
@SerialName("X")
2121
data class X(val i: Int) : B, C
2222

23+
@Serializable
24+
@SerialName("Y")
25+
object Y : B, C
26+
27+
@SerialName("E")
28+
enum class E : B, C {
29+
Q, W
30+
}
31+
2332
@Test
2433
fun testMultipleSuperSealedInterfacesDescriptor() {
2534
val subclasses = A.serializer().descriptor.getElementDescriptor(1).elementDescriptors.map { it.serialName }
26-
assertEquals(listOf("X"), subclasses)
35+
assertEquals(listOf("E", "X", "Y"), subclasses)
2736
}
2837

2938
@Test
@@ -32,8 +41,8 @@ class SealedDiamondTest : JsonTestBase() {
3241
data class Carrier(val a: A, val b: B, val c: C)
3342
assertJsonFormAndRestored(
3443
Carrier.serializer(),
35-
Carrier(X(1), X(2), X(3)),
36-
"""{"a":{"type":"X","i":1},"b":{"type":"X","i":2},"c":{"type":"X","i":3}}"""
44+
Carrier(X(1), X(2), Y),
45+
"""{"a":{"type":"X","i":1},"b":{"type":"X","i":2},"c":{"type":"Y"}}"""
3746
)
3847
}
3948

0 commit comments

Comments
 (0)