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
72 /**
73 * A [SerializersModule] which is empty and always returns `null`.
74 */
75 @Deprecated("Deprecated in the favour of 'EmptySerializersModule()'",
76 level = DeprecationLevel.WARNING,
77 replaceWith = ReplaceWith("EmptySerializersModule()"))
78 @JsName("EmptySerializersModuleLegacyJs") // Compatibility with JS
79 public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
80
81 /**
82 * Returns a combination of two serial modules
83 *
84 * If serializer for some class presents in both modules, a [SerializerAlreadyRegisteredException] is thrown.
85 * To overwrite serializers, use [SerializersModule.overwriteWith] function.
86 */
<lambda>null87 public operator fun SerializersModule.plus(other: SerializersModule): SerializersModule = SerializersModule {
88 include(this@plus)
89 include(other)
90 }
91
92 /**
93 * Returns a combination of two serial modules
94 *
95 * If serializer for some class presents in both modules, result module
96 * will contain serializer from [other] module.
97 */
98 @OptIn(ExperimentalSerializationApi::class)
<lambda>null99 public infix fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule = SerializersModule {
100 include(this@overwriteWith)
101 other.dumpTo(object : SerializersModuleCollector {
102 override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
103 registerSerializer(kClass, ContextualProvider.Argless(serializer), allowOverwrite = true)
104 }
105
106 override fun <T : Any> contextual(
107 kClass: KClass<T>,
108 provider: (serializers: List<KSerializer<*>>) -> KSerializer<*>
109 ) {
110 registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider), allowOverwrite = true)
111 }
112
113 override fun <Base : Any, Sub : Base> polymorphic(
114 baseClass: KClass<Base>,
115 actualClass: KClass<Sub>,
116 actualSerializer: KSerializer<Sub>
117 ) {
118 registerPolymorphicSerializer(baseClass, actualClass, actualSerializer, allowOverwrite = true)
119 }
120
121 override fun <Base : Any> polymorphicDefaultSerializer(
122 baseClass: KClass<Base>,
123 defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
124 ) {
125 registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
126 }
127
128 override fun <Base : Any> polymorphicDefaultDeserializer(
129 baseClass: KClass<Base>,
130 defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?
131 ) {
132 registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, allowOverwrite = true)
133 }
134 })
135 }
136
137 // Implementation details below
138
139 /**
140 * A default implementation of [SerializersModule]
141 * which uses hash maps to store serializers associated with KClasses.
142 */
143 @Suppress("UNCHECKED_CAST")
144 @OptIn(ExperimentalSerializationApi::class)
145 internal class SerialModuleImpl(
146 private val class2ContextualFactory: Map<KClass<*>, ContextualProvider>,
147 @JvmField val polyBase2Serializers: Map<KClass<*>, Map<KClass<*>, KSerializer<*>>>,
148 private val polyBase2DefaultSerializerProvider: Map<KClass<*>, PolymorphicSerializerProvider<*>>,
149 private val polyBase2NamedSerializers: Map<KClass<*>, Map<String, KSerializer<*>>>,
150 private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>
151 ) : SerializersModule() {
152
getPolymorphicnull153 override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
154 if (!baseClass.isInstance(value)) return null
155 // Registered
156 val registered = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy<T>
157 if (registered != null) return registered
158 // Default
159 return (polyBase2DefaultSerializerProvider[baseClass] as? PolymorphicSerializerProvider<T>)?.invoke(value)
160 }
161
getPolymorphicnull162 override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>? {
163 // Registered
164 val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer<out T>
165 if (registered != null) return registered
166 // Default
167 return (polyBase2DefaultDeserializerProvider[baseClass] as? PolymorphicDeserializerProvider<T>)?.invoke(serializedClassName)
168 }
169
getContextualnull170 override fun <T : Any> getContextual(kClass: KClass<T>, typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<T>? {
171 return (class2ContextualFactory[kClass]?.invoke(typeArgumentsSerializers)) as? KSerializer<T>?
172 }
173
dumpTonull174 override fun dumpTo(collector: SerializersModuleCollector) {
175 class2ContextualFactory.forEach { (kclass, serial) ->
176 when (serial) {
177 is ContextualProvider.Argless -> collector.contextual(
178 kclass as KClass<Any>,
179 serial.serializer as KSerializer<Any>
180 )
181 is ContextualProvider.WithTypeArguments -> collector.contextual(kclass, serial.provider)
182 }
183 }
184
185 polyBase2Serializers.forEach { (baseClass, classMap) ->
186 classMap.forEach { (actualClass, serializer) ->
187 collector.polymorphic(
188 baseClass as KClass<Any>,
189 actualClass as KClass<Any>,
190 serializer.cast()
191 )
192 }
193 }
194
195 polyBase2DefaultSerializerProvider.forEach { (baseClass, provider) ->
196 collector.polymorphicDefaultSerializer(baseClass as KClass<Any>, provider as (PolymorphicSerializerProvider<Any>))
197 }
198
199 polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
200 collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<out Any>))
201 }
202 }
203 }
204
205 internal typealias PolymorphicDeserializerProvider<Base> = (className: String?) -> DeserializationStrategy<Base>?
206 internal typealias PolymorphicSerializerProvider<Base> = (value: Base) -> SerializationStrategy<Base>?
207
208 /** This class is needed to support re-registering the same static (argless) serializers:
209 *
210 * ```
211 * val m1 = serializersModuleOf(A::class, A.serializer())
212 * val m2 = serializersModuleOf(A::class, A.serializer())
213 * val aggregate = m1 + m2 // should not throw
214 * ```
215 */
216 internal sealed class ContextualProvider {
invokenull217 abstract operator fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*>
218
219 class Argless(val serializer: KSerializer<*>) : ContextualProvider() {
220 override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> = serializer
221
222 override fun equals(other: Any?): Boolean = other is Argless && other.serializer == this.serializer
223
224 override fun hashCode(): Int = serializer.hashCode()
225 }
226
227 class WithTypeArguments(val provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>) :
228 ContextualProvider() {
invokenull229 override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> =
230 provider(typeArgumentsSerializers)
231 }
232
233 }
234