1 /*
<lambda>null2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5 package kotlinx.serialization.modules
6
7 import kotlinx.serialization.*
8 import kotlinx.serialization.internal.*
9 import kotlin.js.*
10 import kotlin.jvm.*
11 import kotlin.reflect.*
12
13 /**
14 * [SerializersModule] is a collection of serializers used by [ContextualSerializer] and [PolymorphicSerializer]
15 * to override or provide serializers at the runtime, whereas at the compile-time they provided by the serialization plugin.
16 * It can be considered as a map where serializers can be found using their statically known KClasses.
17 *
18 * To enable runtime serializers resolution, one of the special annotations must be used on target types
19 * ([Polymorphic] or [Contextual]), and a serial module with serializers should be used during construction of [SerialFormat].
20 *
21 * Serializers module can be built with `SerializersModule {}` builder function.
22 * Empty module can be obtained with `EmptySerializersModule()` factory function.
23 *
24 * @see Contextual
25 * @see Polymorphic
26 */
27 public sealed class SerializersModule {
28
29 @ExperimentalSerializationApi
30 @Deprecated(
31 "Deprecated in favor of overload with default parameter",
32 ReplaceWith("getContextual(kclass)"),
33 DeprecationLevel.HIDDEN
34 ) // Was experimental since 1.0.0, HIDDEN in 1.2.0 in a backwards-compatible manner
35 public fun <T : Any> getContextual(kclass: KClass<T>): KSerializer<T>? =
36 getContextual(kclass, emptyList())
37
38 /**
39 * Returns a contextual serializer associated with a given [kClass].
40 * If given class has generic parameters and module has provider for [kClass],
41 * [typeArgumentsSerializers] are used to create serializer.
42 * This method is used in context-sensitive operations on a property marked with [Contextual] by a [ContextualSerializer].
43 *
44 * @see SerializersModuleBuilder.contextual
45 */
46 @ExperimentalSerializationApi
47 public abstract fun <T : Any> getContextual(
48 kClass: KClass<T>,
49 typeArgumentsSerializers: List<KSerializer<*>> = emptyList()
50 ): KSerializer<T>?
51
52 /**
53 * Returns a polymorphic serializer registered for a class of the given [value] in the scope of [baseClass].
54 */
55 @ExperimentalSerializationApi
56 public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>?
57
58 /**
59 * Returns a polymorphic deserializer registered for a [serializedClassName] in the scope of [baseClass]
60 * or default value constructed from [serializedClassName] if a default serializer provider was registered.
61 */
62 @ExperimentalSerializationApi
63 public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>?
64
65 /**
66 * Copies contents of this module to the given [collector].
67 */
68 @ExperimentalSerializationApi
69 public abstract fun dumpTo(collector: SerializersModuleCollector)
70
71 @InternalSerializationApi
72 internal abstract val hasInterfaceContextualSerializers: Boolean
73 }
74
75 /**
76 * A [SerializersModule] which is empty and always returns `null`.
77 */
78 @Deprecated("Deprecated in the favour of 'EmptySerializersModule()'",
79 level = DeprecationLevel.WARNING,
80 replaceWith = ReplaceWith("EmptySerializersModule()"))
81 @JsName("EmptySerializersModuleLegacyJs") // Compatibility with JS
82 public val EmptySerializersModule: SerializersModule = SerialModuleImpl(
83 emptyMap(),
84 emptyMap(),
85 emptyMap(),
86 emptyMap(),
87 emptyMap(),
88 false
89 )
90
91 /**
92 * Returns a combination of two serial modules
93 *
94 * If serializer for some class presents in both modules, a [SerializerAlreadyRegisteredException] is thrown.
95 * To overwrite serializers, use [SerializersModule.overwriteWith] function.
96 */
<lambda>null97 public operator fun SerializersModule.plus(other: SerializersModule): SerializersModule = SerializersModule {
98 include(this@plus)
99 include(other)
100 }
101
102 /**
103 * Returns a combination of two serial modules
104 *
105 * If serializer for some class presents in both modules, result module
106 * will contain serializer from [other] module.
107 */
108 @OptIn(ExperimentalSerializationApi::class)
<lambda>null109 public infix fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule = SerializersModule {
110 include(this@overwriteWith)
111 other.dumpTo(object : SerializersModuleCollector {
112 override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
113 registerSerializer(kClass, ContextualProvider.Argless(serializer), allowOverwrite = true)
114 }
115
116 override fun <T : Any> contextual(
117 kClass: KClass<T>,
118 provider: (serializers: List<KSerializer<*>>) -> KSerializer<*>
119 ) {
120 registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider), allowOverwrite = true)
121 }
122
123 override fun <Base : Any, Sub : Base> polymorphic(
124 baseClass: KClass<Base>,
125 actualClass: KClass<Sub>,
126 actualSerializer: KSerializer<Sub>
127 ) {
128 registerPolymorphicSerializer(baseClass, actualClass, actualSerializer, allowOverwrite = true)
129 }
130
131 override fun <Base : Any> polymorphicDefaultSerializer(
132 baseClass: KClass<Base>,
133 defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
134 ) {
135 registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
136 }
137
138 override fun <Base : Any> polymorphicDefaultDeserializer(
139 baseClass: KClass<Base>,
140 defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?
141 ) {
142 registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, allowOverwrite = true)
143 }
144 })
145 }
146
147 // Implementation details below
148
149 /**
150 * A default implementation of [SerializersModule]
151 * which uses hash maps to store serializers associated with KClasses.
152 */
153 @Suppress("UNCHECKED_CAST")
154 @OptIn(ExperimentalSerializationApi::class)
155 internal class SerialModuleImpl(
156 private val class2ContextualFactory: Map<KClass<*>, ContextualProvider>,
157 @JvmField val polyBase2Serializers: Map<KClass<*>, Map<KClass<*>, KSerializer<*>>>,
158 private val polyBase2DefaultSerializerProvider: Map<KClass<*>, PolymorphicSerializerProvider<*>>,
159 private val polyBase2NamedSerializers: Map<KClass<*>, Map<String, KSerializer<*>>>,
160 private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>,
161 internal override val hasInterfaceContextualSerializers: Boolean
162 ) : SerializersModule() {
163
getPolymorphicnull164 override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
165 if (!baseClass.isInstance(value)) return null
166 // Registered
167 val registered = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy<T>
168 if (registered != null) return registered
169 // Default
170 return (polyBase2DefaultSerializerProvider[baseClass] as? PolymorphicSerializerProvider<T>)?.invoke(value)
171 }
172
getPolymorphicnull173 override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>? {
174 // Registered
175 val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer<out T>
176 if (registered != null) return registered
177 // Default
178 return (polyBase2DefaultDeserializerProvider[baseClass] as? PolymorphicDeserializerProvider<T>)?.invoke(serializedClassName)
179 }
180
getContextualnull181 override fun <T : Any> getContextual(kClass: KClass<T>, typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<T>? {
182 return (class2ContextualFactory[kClass]?.invoke(typeArgumentsSerializers)) as? KSerializer<T>?
183 }
184
dumpTonull185 override fun dumpTo(collector: SerializersModuleCollector) {
186 class2ContextualFactory.forEach { (kclass, serial) ->
187 when (serial) {
188 is ContextualProvider.Argless -> collector.contextual(
189 kclass as KClass<Any>,
190 serial.serializer as KSerializer<Any>
191 )
192 is ContextualProvider.WithTypeArguments -> collector.contextual(kclass, serial.provider)
193 }
194 }
195
196 polyBase2Serializers.forEach { (baseClass, classMap) ->
197 classMap.forEach { (actualClass, serializer) ->
198 collector.polymorphic(
199 baseClass as KClass<Any>,
200 actualClass as KClass<Any>,
201 serializer.cast()
202 )
203 }
204 }
205
206 polyBase2DefaultSerializerProvider.forEach { (baseClass, provider) ->
207 collector.polymorphicDefaultSerializer(baseClass as KClass<Any>, provider as (PolymorphicSerializerProvider<Any>))
208 }
209
210 polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
211 collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<Any>))
212 }
213 }
214 }
215
216 internal typealias PolymorphicDeserializerProvider<Base> = (className: String?) -> DeserializationStrategy<Base>?
217 internal typealias PolymorphicSerializerProvider<Base> = (value: Base) -> SerializationStrategy<Base>?
218
219 /** This class is needed to support re-registering the same static (argless) serializers:
220 *
221 * ```
222 * val m1 = serializersModuleOf(A::class, A.serializer())
223 * val m2 = serializersModuleOf(A::class, A.serializer())
224 * val aggregate = m1 + m2 // should not throw
225 * ```
226 */
227 internal sealed class ContextualProvider {
invokenull228 abstract operator fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*>
229
230 class Argless(val serializer: KSerializer<*>) : ContextualProvider() {
231 override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> = serializer
232
233 override fun equals(other: Any?): Boolean = other is Argless && other.serializer == this.serializer
234
235 override fun hashCode(): Int = serializer.hashCode()
236 }
237
238 class WithTypeArguments(val provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>) :
239 ContextualProvider() {
invokenull240 override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> =
241 provider(typeArgumentsSerializers)
242 }
243
244 }
245