1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
/*
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.internal
import kotlinx.serialization.*
import kotlinx.serialization.encoding.*
import kotlin.jvm.*
import kotlin.reflect.*
/**
* Base class for providing multiplatform polymorphic serialization.
*
* This class cannot be implemented by library users. To learn how to use it for your case,
* please refer to [PolymorphicSerializer] for interfaces/abstract classes and [SealedClassSerializer] for sealed classes.
*
* By default, without special support from [Encoder], polymorphic types are serialized as list with
* two elements: class [serial name][SerialDescriptor.serialName] (String) and the object itself.
* Serial name equals to fully-qualified class name by default and can be changed via @[SerialName] annotation.
*/
@InternalSerializationApi
@OptIn(ExperimentalSerializationApi::class)
public abstract class AbstractPolymorphicSerializer<T : Any> internal constructor() : KSerializer<T> {
/**
* Base class for all classes that this polymorphic serializer can serialize or deserialize.
*/
public abstract val baseClass: KClass<T>
public final override fun serialize(encoder: Encoder, value: T) {
val actualSerializer = findPolymorphicSerializer(encoder, value)
encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, actualSerializer.descriptor.serialName)
encodeSerializableElement(descriptor, 1, actualSerializer.cast(), value)
}
}
public final override fun deserialize(decoder: Decoder): T = decoder.decodeStructure(descriptor) {
var klassName: String? = null
var value: Any? = null
if (decodeSequentially()) {
return@decodeStructure decodeSequentially(this)
}
mainLoop@ while (true) {
when (val index = decodeElementIndex(descriptor)) {
CompositeDecoder.DECODE_DONE -> {
break@mainLoop
}
0 -> {
klassName = decodeStringElement(descriptor, index)
}
1 -> {
klassName = requireNotNull(klassName) { "Cannot read polymorphic value before its type token" }
val serializer = findPolymorphicSerializer(this, klassName)
value = decodeSerializableElement(descriptor, index, serializer)
}
else -> throw SerializationException(
"Invalid index in polymorphic deserialization of " +
(klassName ?: "unknown class") +
"\n Expected 0, 1 or DECODE_DONE(-1), but found $index"
)
}
}
@Suppress("UNCHECKED_CAST")
requireNotNull(value) { "Polymorphic value has not been read for class $klassName" } as T
}
private fun decodeSequentially(compositeDecoder: CompositeDecoder): T {
val klassName = compositeDecoder.decodeStringElement(descriptor, 0)
val serializer = findPolymorphicSerializer(compositeDecoder, klassName)
return compositeDecoder.decodeSerializableElement(descriptor, 1, serializer)
}
/**
* Lookups an actual serializer for given [klassName] withing the current [base class][baseClass].
* May use context from the [decoder].
*/
@InternalSerializationApi
public open fun findPolymorphicSerializerOrNull(
decoder: CompositeDecoder,
klassName: String?
): DeserializationStrategy<T>? = decoder.serializersModule.getPolymorphic(baseClass, klassName)
/**
* Lookups an actual serializer for given [value] within the current [base class][baseClass].
* May use context from the [encoder].
*/
@InternalSerializationApi
public open fun findPolymorphicSerializerOrNull(
encoder: Encoder,
value: T
): SerializationStrategy<T>? =
encoder.serializersModule.getPolymorphic(baseClass, value)
}
@JvmName("throwSubtypeNotRegistered")
internal fun throwSubtypeNotRegistered(subClassName: String?, baseClass: KClass<*>): Nothing {
val scope = "in the polymorphic scope of '${baseClass.simpleName}'"
throw SerializationException(
if (subClassName == null)
"Class discriminator was missing and no default serializers were registered $scope."
else
"Serializer for subclass '$subClassName' is not found $scope.\n" +
"Check if class with serial name '$subClassName' exists and serializer is registered in a corresponding SerializersModule.\n" +
"To be registered automatically, class '$subClassName' has to be '@Serializable', and the base class '${baseClass.simpleName}' has to be sealed and '@Serializable'."
)
}
@JvmName("throwSubtypeNotRegistered")
internal fun throwSubtypeNotRegistered(subClass: KClass<*>, baseClass: KClass<*>): Nothing =
throwSubtypeNotRegistered(subClass.simpleName ?: "$subClass", baseClass)
|