• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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