1 /*
2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4 package kotlinx.serialization.modules
5
6 import kotlinx.serialization.*
7 import kotlinx.serialization.internal.*
8 import kotlin.jvm.*
9 import kotlin.reflect.*
10
11 /**
12 * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer].
13 */
serializersModuleOfnull14 public fun <T : Any> serializersModuleOf(kClass: KClass<T>, serializer: KSerializer<T>): SerializersModule =
15 SerializersModule { contextual(kClass, serializer) }
16
17 /**
18 * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer].
19 */
serializersModuleOfnull20 public inline fun <reified T : Any> serializersModuleOf(serializer: KSerializer<T>): SerializersModule =
21 serializersModuleOf(T::class, serializer)
22
23 /**
24 * A builder function for creating a [SerializersModule].
25 * Serializers can be added via [SerializersModuleBuilder.contextual] or [SerializersModuleBuilder.polymorphic].
26 * Since [SerializersModuleBuilder] also implements [SerialModuleCollector],
27 * it is possible to copy whole another module to this builder with [SerializersModule.dumpTo]
28 */
29 @Suppress("FunctionName")
30 public inline fun SerializersModule(builderAction: SerializersModuleBuilder.() -> Unit): SerializersModule {
31 val builder = SerializersModuleBuilder()
32 builder.builderAction()
33 return builder.build()
34 }
35
36 /**
37 * A [SerializersModule] which is empty and returns `null` from each method.
38 */
39 @Suppress("FunctionName")
EmptySerializersModulenull40 public fun EmptySerializersModule(): SerializersModule = @Suppress("DEPRECATION") EmptySerializersModule
41
42 /**
43 * A builder class for [SerializersModule] DSL. To create an instance of builder, use [SerializersModule] factory function.
44 */
45 @OptIn(ExperimentalSerializationApi::class)
46 public class SerializersModuleBuilder @PublishedApi internal constructor() : SerializersModuleCollector {
47 private val class2ContextualProvider: MutableMap<KClass<*>, ContextualProvider> = hashMapOf()
48 private val polyBase2Serializers: MutableMap<KClass<*>, MutableMap<KClass<*>, KSerializer<*>>> = hashMapOf()
49 private val polyBase2DefaultSerializerProvider: MutableMap<KClass<*>, PolymorphicSerializerProvider<*>> = hashMapOf()
50 private val polyBase2NamedSerializers: MutableMap<KClass<*>, MutableMap<String, KSerializer<*>>> = hashMapOf()
51 private val polyBase2DefaultDeserializerProvider: MutableMap<KClass<*>, PolymorphicDeserializerProvider<*>> = hashMapOf()
52 private var hasInterfaceContextualSerializers: Boolean = false
53
54 /**
55 * Adds [serializer] associated with given [kClass] for contextual serialization.
56 * If [kClass] has generic type parameters, consider registering provider instead.
57 *
58 * Throws [SerializationException] if a module already has serializer or provider associated with a [kClass].
59 * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
60 */
61 public override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>): Unit =
62 registerSerializer(kClass, ContextualProvider.Argless(serializer))
63
64 /**
65 * Registers [provider] associated with given generic [kClass] for contextual serialization.
66 * When a serializer is requested from a module, provider is being called with type arguments serializers
67 * of the particular [kClass] usage.
68 *
69 * Example:
70 * ```
71 * class Holder(@Contextual val boxI: Box<Int>, @Contextual val boxS: Box<String>)
72 *
73 * val module = SerializersModule {
74 * // args[0] contains Int.serializer() or String.serializer(), depending on the property
75 * contextual(Box::class) { args -> BoxSerializer(args[0]) }
76 * }
77 * ```
78 *
79 * Throws [SerializationException] if a module already has provider or serializer associated with a [kClass].
80 * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
81 */
82 public override fun <T : Any> contextual(
83 kClass: KClass<T>,
84 provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
85 ): Unit = registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider))
86
87 /**
88 * Adds [serializer][actualSerializer] associated with given [actualClass] in the scope of [baseClass] for polymorphic serialization.
89 * Throws [SerializationException] if a module already has serializer associated with a [actualClass].
90 * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
91 */
92 public override fun <Base : Any, Sub : Base> polymorphic(
93 baseClass: KClass<Base>,
94 actualClass: KClass<Sub>,
95 actualSerializer: KSerializer<Sub>
96 ) {
97 registerPolymorphicSerializer(baseClass, actualClass, actualSerializer)
98 }
99
100 /**
101 * Adds a default serializers provider associated with the given [baseClass] to the resulting module.
102 * [defaultSerializerProvider] is invoked when no polymorphic serializers for `value` in the scope of [baseClass] were found.
103 *
104 * Default serializers provider affects only serialization process. To affect deserialization process, use
105 * [SerializersModuleBuilder.polymorphicDefaultDeserializer].
106 *
107 * [defaultSerializerProvider] can be stateful and lookup a serializer for the missing type dynamically.
108 */
109 public override fun <Base : Any> polymorphicDefaultSerializer(
110 baseClass: KClass<Base>,
111 defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
112 ) {
113 registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, false)
114 }
115
116 /**
117 * Adds a default deserializers provider associated with the given [baseClass] to the resulting module.
118 * [defaultDeserializerProvider] is invoked when no polymorphic serializers associated with the `className`
119 * in the scope of [baseClass] were found. `className` could be `null` for formats that support nullable class discriminators
120 * (currently only `Json` with `useArrayPolymorphism` set to `false`).
121 *
122 * Default deserializers provider affects only deserialization process. To affect serialization process, use
123 * [SerializersModuleBuilder.polymorphicDefaultSerializer].
124 *
125 * [defaultDeserializerProvider] can be stateful and lookup a serializer for the missing type dynamically.
126 *
127 * @see PolymorphicModuleBuilder.defaultDeserializer
128 */
129 public override fun <Base : Any> polymorphicDefaultDeserializer(
130 baseClass: KClass<Base>,
131 defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?
132 ) {
133 registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, false)
134 }
135
136 /**
137 * Copies the content of [module] module into the current builder.
138 */
139 public fun include(module: SerializersModule) {
140 module.dumpTo(this)
141 }
142
143 @JvmName("registerSerializer") // Don't mangle method name for prettier stack traces
144 internal fun <T : Any> registerSerializer(
145 forClass: KClass<T>,
146 provider: ContextualProvider,
147 allowOverwrite: Boolean = false
148 ) {
149 if (!allowOverwrite) {
150 val previous = class2ContextualProvider[forClass]
151 if (previous != null && previous != provider) {
152 // How can we provide meaningful name for WithTypeArgumentsProvider ?
153 throw SerializerAlreadyRegisteredException(
154 "Contextual serializer or serializer provider for $forClass already registered in this module"
155 )
156 }
157 }
158 class2ContextualProvider[forClass] = provider
159 if (forClass.isInterface()) hasInterfaceContextualSerializers = true
160 }
161
162 @JvmName("registerDefaultPolymorphicSerializer") // Don't mangle method name for prettier stack traces
163 internal fun <Base : Any> registerDefaultPolymorphicSerializer(
164 baseClass: KClass<Base>,
165 defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?,
166 allowOverwrite: Boolean
167 ) {
168 val previous = polyBase2DefaultSerializerProvider[baseClass]
169 if (previous != null && previous != defaultSerializerProvider && !allowOverwrite) {
170 throw IllegalArgumentException("Default serializers provider for $baseClass is already registered: $previous")
171 }
172 polyBase2DefaultSerializerProvider[baseClass] = defaultSerializerProvider
173 }
174
175 @JvmName("registerDefaultPolymorphicDeserializer") // Don't mangle method name for prettier stack traces
176 internal fun <Base : Any> registerDefaultPolymorphicDeserializer(
177 baseClass: KClass<Base>,
178 defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?,
179 allowOverwrite: Boolean
180 ) {
181 val previous = polyBase2DefaultDeserializerProvider[baseClass]
182 if (previous != null && previous != defaultDeserializerProvider && !allowOverwrite) {
183 throw IllegalArgumentException("Default deserializers provider for $baseClass is already registered: $previous")
184 }
185 polyBase2DefaultDeserializerProvider[baseClass] = defaultDeserializerProvider
186 }
187
188 @JvmName("registerPolymorphicSerializer") // Don't mangle method name for prettier stack traces
189 internal fun <Base : Any, Sub : Base> registerPolymorphicSerializer(
190 baseClass: KClass<Base>,
191 concreteClass: KClass<Sub>,
192 concreteSerializer: KSerializer<Sub>,
193 allowOverwrite: Boolean = false
194 ) {
195 // Check for overwrite
196 val name = concreteSerializer.descriptor.serialName
197 val baseClassSerializers = polyBase2Serializers.getOrPut(baseClass, ::hashMapOf)
198 val previousSerializer = baseClassSerializers[concreteClass]
199 val names = polyBase2NamedSerializers.getOrPut(baseClass, ::hashMapOf)
200 if (allowOverwrite) {
201 // Remove previous serializers from name mapping
202 if (previousSerializer != null) {
203 names.remove(previousSerializer.descriptor.serialName)
204 }
205 // Update mappings
206 baseClassSerializers[concreteClass] = concreteSerializer
207 names[name] = concreteSerializer
208 return
209 }
210 // Overwrite prohibited
211 if (previousSerializer != null) {
212 if (previousSerializer != concreteSerializer) {
213 throw SerializerAlreadyRegisteredException(baseClass, concreteClass)
214 } else {
215 // Cleanup name mapping
216 names.remove(previousSerializer.descriptor.serialName)
217 }
218 }
219 val previousByName = names[name]
220 if (previousByName != null) {
221 val conflictingClass = polyBase2Serializers[baseClass]!!.asSequence().find { it.value === previousByName }
222 throw IllegalArgumentException(
223 "Multiple polymorphic serializers for base class '$baseClass' " +
224 "have the same serial name '$name': '$concreteClass' and '$conflictingClass'"
225 )
226 }
227 // Overwrite if no conflicts
228 baseClassSerializers[concreteClass] = concreteSerializer
229 names[name] = concreteSerializer
230 }
231
232 @PublishedApi
233 internal fun build(): SerializersModule =
234 SerialModuleImpl(class2ContextualProvider, polyBase2Serializers, polyBase2DefaultSerializerProvider, polyBase2NamedSerializers, polyBase2DefaultDeserializerProvider, hasInterfaceContextualSerializers)
235 }
236
237 /**
238 * Adds [serializer] associated with given type [T] for contextual serialization.
239 * Throws [SerializationException] if a module already has serializer associated with the given type.
240 * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
241 */
contextualnull242 public inline fun <reified T : Any> SerializersModuleBuilder.contextual(serializer: KSerializer<T>): Unit =
243 contextual(T::class, serializer)
244
245 /**
246 * Creates a builder to register subclasses of a given [baseClass] for polymorphic serialization.
247 * If [baseSerializer] is not null, registers it as a serializer for [baseClass],
248 * which is useful if the base class is serializable itself. To register subclasses,
249 * [PolymorphicModuleBuilder.subclass] builder function can be used.
250 *
251 * If a serializer already registered for the given KClass in the given scope, an [IllegalArgumentException] is thrown.
252 * To override registered serializers, combine built module with another using [SerializersModule.overwriteWith].
253 *
254 * @see PolymorphicSerializer
255 */
256 public inline fun <Base : Any> SerializersModuleBuilder.polymorphic(
257 baseClass: KClass<Base>,
258 baseSerializer: KSerializer<Base>? = null,
259 builderAction: PolymorphicModuleBuilder<Base>.() -> Unit = {}
260 ) {
261 val builder = PolymorphicModuleBuilder(baseClass, baseSerializer)
262 builder.builderAction()
263 builder.buildTo(this)
264 }
265
266 private class SerializerAlreadyRegisteredException internal constructor(msg: String) : IllegalArgumentException(msg) {
267 internal constructor(
268 baseClass: KClass<*>,
269 concreteClass: KClass<*>
270 ) : this("Serializer for $concreteClass already registered in the scope of $baseClass")
271 }
272