• 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 @file:Suppress("DEPRECATION_ERROR", "UNCHECKED_CAST")
5 @file:JvmMultifileClass
6 @file:JvmName("SerializersKt")
7 
8 package kotlinx.serialization
9 
10 import kotlinx.serialization.builtins.*
11 import kotlinx.serialization.builtins.MapEntrySerializer
12 import kotlinx.serialization.builtins.PairSerializer
13 import kotlinx.serialization.builtins.TripleSerializer
14 import kotlinx.serialization.internal.*
15 import kotlinx.serialization.modules.*
16 import kotlin.jvm.*
17 import kotlin.reflect.*
18 
19 /**
20  * Retrieves a serializer for the given type [T].
21  * This overload is a reified version of `serializer(KType)`.
22  *
23  * This overload works with full type information, including type arguments and nullability,
24  * and is a recommended way to retrieve a serializer.
25  * For example, `serializer<List<String?>>()` returns [KSerializer] that is able
26  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
27  *
28  * Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
29  * Star projections in [T]'s type arguments are prohibited.
30  *
31  * @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
32  * @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
33  */
serializernull34 public inline fun <reified T> serializer(): KSerializer<T> {
35     return serializer(typeOf<T>()).cast()
36 }
37 
38 /**
39  * Retrieves default serializer for the given type [T] and,
40  * if [T] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
41  *
42  * This overload works with full type information, including type arguments and nullability,
43  * and is a recommended way to retrieve a serializer.
44  * For example, `serializer<List<String?>>()` returns [KSerializer] that is able
45  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
46  *
47  * Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
48  * Star projections in [T]'s type arguments are prohibited.
49  *
50  * @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
51  * @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
52  */
serializernull53 public inline fun <reified T> SerializersModule.serializer(): KSerializer<T> {
54     return serializer(typeOf<T>()).cast()
55 }
56 
57 /**
58  * Creates a serializer for the given [type].
59  * [type] argument is usually obtained with [typeOf] method.
60  *
61  * This overload works with full type information, including type arguments and nullability,
62  * and is a recommended way to retrieve a serializer.
63  * For example, `serializer<typeOf<List<String?>>>()` returns [KSerializer] that is able
64  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
65  *
66  * Variance of [type]'s type arguments is not used by the serialization and is not taken into account.
67  * Star projections in [type]'s arguments are prohibited.
68  *
69  * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
70  * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
71  */
serializernull72 public fun serializer(type: KType): KSerializer<Any?> = EmptySerializersModule().serializer(type)
73 
74 
75 /**
76  * Retrieves serializer for the given [kClass].
77  * This method uses platform-specific reflection available.
78  *
79  * If [kClass] is a parametrized type then it is necessary to pass serializers for generic parameters in the [typeArgumentsSerializers].
80  * The nullability of returned serializer is specified using the [isNullable].
81  *
82  * Note that it is impossible to create an array serializer with this method,
83  * as array serializer needs additional information: type token for an element type.
84  * To create array serializer, use overload with [KType] or [ArraySerializer] directly.
85  *
86  * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
87  *
88  * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable)
89  * @throws SerializationException if [kClass] is a `kotlin.Array`
90  * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
91  */
92 @ExperimentalSerializationApi
93 public fun serializer(
94     kClass: KClass<*>,
95     typeArgumentsSerializers: List<KSerializer<*>>,
96     isNullable: Boolean
97 ): KSerializer<Any?> = EmptySerializersModule().serializer(kClass, typeArgumentsSerializers, isNullable)
98 
99 /**
100  * Creates a serializer for the given [type] if possible.
101  * [type] argument is usually obtained with [typeOf] method.
102  *
103  * This overload works with full type information, including type arguments and nullability,
104  * and is a recommended way to retrieve a serializer.
105  * For example, `serializerOrNull<typeOf<List<String?>>>()` returns [KSerializer] that is able
106  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
107  *
108  * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
109  * Star projections in [type]'s arguments are prohibited.
110  *
111  * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
112  * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
113  */
114 public fun serializerOrNull(type: KType): KSerializer<Any?>? = EmptySerializersModule().serializerOrNull(type)
115 
116 /**
117  * Retrieves default serializer for the given [type] and,
118  * if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
119  * [type] argument is usually obtained with [typeOf] method.
120  *
121  * This overload works with full type information, including type arguments and nullability,
122  * and is a recommended way to retrieve a serializer.
123  * For example, `serializer<typeOf<List<String?>>>()` returns [KSerializer] that is able
124  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
125  *
126  * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
127  * Star projections in [type]'s arguments are prohibited.
128  *
129  * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
130  * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
131  */
132 public fun SerializersModule.serializer(type: KType): KSerializer<Any?> =
133     serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.kclass()
134         .platformSpecificSerializerNotRegistered()
135 
136 
137 /**
138  * Retrieves serializer for the given [kClass] and,
139  * if [kClass] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
140  * This method uses platform-specific reflection available.
141  *
142  * If [kClass] is a parametrized type then it is necessary to pass serializers for generic parameters in the [typeArgumentsSerializers].
143  * The nullability of returned serializer is specified using the [isNullable].
144  *
145  * Note that it is impossible to create an array serializer with this method,
146  * as array serializer needs additional information: type token for an element type.
147  * To create array serializer, use overload with [KType] or [ArraySerializer] directly.
148  *
149  * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
150  *
151  * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable and is not registered in [this] module)
152  * @throws SerializationException if [kClass] is a `kotlin.Array`
153  * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
154  */
155 @ExperimentalSerializationApi
156 public fun SerializersModule.serializer(
157     kClass: KClass<*>,
158     typeArgumentsSerializers: List<KSerializer<*>>,
159     isNullable: Boolean
160 ): KSerializer<Any?> =
161     serializerByKClassImpl(kClass as KClass<Any>, typeArgumentsSerializers as List<KSerializer<Any?>>, isNullable)
162         ?: kClass.platformSpecificSerializerNotRegistered()
163 
164 /**
165  * Retrieves default serializer for the given [type] and,
166  * if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
167  * [type] argument is usually obtained with [typeOf] method.
168  *
169  * This overload works with full type information, including type arguments and nullability,
170  * and is a recommended way to retrieve a serializer.
171  * For example, `serializerOrNull<typeOf<List<String?>>>()` returns [KSerializer] that is able
172  * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
173  *
174  * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
175  * Star projections in [type]'s arguments are prohibited.
176  *
177  * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module).
178  * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
179  */
180 public fun SerializersModule.serializerOrNull(type: KType): KSerializer<Any?>? =
181     serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = false)
182 
183 @OptIn(ExperimentalSerializationApi::class)
184 private fun SerializersModule.serializerByKTypeImpl(
185     type: KType,
186     failOnMissingTypeArgSerializer: Boolean
187 ): KSerializer<Any?>? {
188     val rootClass = type.kclass()
189     val isNullable = type.isMarkedNullable
190     val typeArguments = type.arguments.map(KTypeProjection::typeOrThrow)
191 
192     val cachedSerializer = if (typeArguments.isEmpty()) {
193         findCachedSerializer(rootClass, isNullable)
194     } else {
195         findParametrizedCachedSerializer(rootClass, typeArguments, isNullable).getOrNull()
196     }
197     cachedSerializer?.let { return it }
198 
199     // slow path to find contextual serializers in serializers module
200     val contextualSerializer: KSerializer<out Any?>? = if (typeArguments.isEmpty()) {
201         getContextual(rootClass)
202     } else {
203         val serializers = serializersForParameters(typeArguments, failOnMissingTypeArgSerializer) ?: return null
204         // first, we look among the built-in serializers, because the parameter could be contextual
205         rootClass.parametrizedSerializerOrNull(serializers) { typeArguments[0].classifier }
206             ?: getContextual(
207                 rootClass,
208                 serializers
209             )
210     }
211     return contextualSerializer?.cast<Any>()?.nullable(isNullable)
212 }
213 
214 @OptIn(ExperimentalSerializationApi::class)
serializerByKClassImplnull215 private fun SerializersModule.serializerByKClassImpl(
216     rootClass: KClass<Any>,
217     typeArgumentsSerializers: List<KSerializer<Any?>>,
218     isNullable: Boolean
219 ): KSerializer<Any?>? {
220     val serializer = if (typeArgumentsSerializers.isEmpty()) {
221         rootClass.serializerOrNull() ?: getContextual(rootClass)
222     } else {
223         try {
224             rootClass.parametrizedSerializerOrNull(typeArgumentsSerializers) {
225                 throw SerializationException("It is not possible to retrieve an array serializer using KClass alone, use KType instead or ArraySerializer factory")
226             } ?: getContextual(
227                 rootClass,
228                 typeArgumentsSerializers
229             )
230         } catch (e: IndexOutOfBoundsException) {
231             throw SerializationException("Unable to retrieve a serializer, the number of passed type serializers differs from the actual number of generic parameters", e)
232         }
233     }
234 
235     return serializer?.cast<Any>()?.nullable(isNullable)
236 }
237 
238 /**
239  * Returns null only if `failOnMissingTypeArgSerializer == false` and at least one parameter serializer not found.
240  */
serializersForParametersnull241 internal fun SerializersModule.serializersForParameters(
242     typeArguments: List<KType>,
243     failOnMissingTypeArgSerializer: Boolean
244 ): List<KSerializer<Any?>>? {
245     val serializers = if (failOnMissingTypeArgSerializer) {
246         typeArguments.map { serializer(it) }
247     } else {
248         typeArguments.map { serializerOrNull(it) ?: return null }
249     }
250     return serializers
251 }
252 
253 /**
254  * Retrieves a [KSerializer] for the given [KClass].
255  * The given class must be annotated with [Serializable] or be one of the built-in types.
256  *
257  * This method uses platform-specific reflection available for the given erased `KClass`
258  * and is not recommended to use this method for anything, but last-ditch resort, e.g.
259  * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
260  *
261  * The recommended way to retrieve the serializer is inline [serializer] function and [`serializer(KType)`][serializer]
262  *
263  * This API is not guaranteed to work consistently across different platforms or
264  * to work in cases that slightly differ from "plain @Serializable class" and have platform and reflection specific limitations.
265  *
266  * ### Constraints
267  * This paragraph explains known (but not all!) constraints of the `serializer()` implementation.
268  * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
269  *
270  * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
271  * * Serializers for classes with generic parameters are ignored by this method
272  * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
273  * * Serializers for classes with named companion objects  are not lookuped consistently
274  *
275  * @throws SerializationException if serializer can't be found.
276  */
277 @InternalSerializationApi
serializernull278 public fun <T : Any> KClass<T>.serializer(): KSerializer<T> = serializerOrNull() ?: serializerNotRegistered()
279 
280 /**
281  * Retrieves a [KSerializer] for the given [KClass] or returns `null` if none is found.
282  * The given class must be annotated with [Serializable] or be one of the built-in types.
283  * This method uses platform-specific reflection available for the given erased `KClass`
284  * and it is not recommended to use this method for anything, but last-ditch resort, e.g.
285  * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
286  *
287  * This API is not guaranteed to work consistently across different platforms or
288  * to work in cases that slightly differ from "plain @Serializable class".
289  *
290  * ### Constraints
291  * This paragraph explains known (but not all!) constraints of the `serializerOrNull()` implementation.
292  * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
293  *
294  * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
295  * * Serializers for classes with generic parameters are ignored by this method
296  * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
297  * * Serializers for classes with named companion objects  are not lookuped consistently
298  */
299 @InternalSerializationApi
300 public fun <T : Any> KClass<T>.serializerOrNull(): KSerializer<T>? =
301     compiledSerializerImpl() ?: builtinSerializerOrNull()
302 
303 internal fun KClass<Any>.parametrizedSerializerOrNull(
304     serializers: List<KSerializer<Any?>>,
305     elementClassifierIfArray: () -> KClassifier?
306 ): KSerializer<out Any>? {
307     // builtin first because some standard parametrized interfaces (e.g. Map) must use builtin serializer but not polymorphic
308     return builtinParametrizedSerializer(serializers, elementClassifierIfArray) ?: compiledParametrizedSerializer(serializers)
309 }
310 
311 
KClassnull312 private fun KClass<Any>.compiledParametrizedSerializer(serializers: List<KSerializer<Any?>>): KSerializer<out Any>? {
313     return constructSerializerForGivenTypeArgs(*serializers.toTypedArray())
314 }
315 
316 @OptIn(ExperimentalSerializationApi::class)
KClassnull317 private fun KClass<Any>.builtinParametrizedSerializer(
318     serializers: List<KSerializer<Any?>>,
319     elementClassifierIfArray: () -> KClassifier?
320 ): KSerializer<out Any>? {
321     return when (this) {
322         Collection::class, List::class, MutableList::class, ArrayList::class -> ArrayListSerializer(serializers[0])
323         HashSet::class -> HashSetSerializer(serializers[0])
324         Set::class, MutableSet::class, LinkedHashSet::class -> LinkedHashSetSerializer(serializers[0])
325         HashMap::class -> HashMapSerializer(serializers[0], serializers[1])
326         Map::class, MutableMap::class, LinkedHashMap::class -> LinkedHashMapSerializer(
327             serializers[0],
328             serializers[1]
329         )
330 
331         Map.Entry::class -> MapEntrySerializer(serializers[0], serializers[1])
332         Pair::class -> PairSerializer(serializers[0], serializers[1])
333         Triple::class -> TripleSerializer(serializers[0], serializers[1], serializers[2])
334         else -> {
335             if (isReferenceArray(this)) {
336                 ArraySerializer(elementClassifierIfArray() as KClass<Any>, serializers[0])
337             } else {
338                 null
339             }
340         }
341     }
342 }
343 
nullablenull344 private fun <T : Any> KSerializer<T>.nullable(shouldBeNullable: Boolean): KSerializer<T?> {
345     if (shouldBeNullable) return nullable
346     return this as KSerializer<T?>
347 }
348 
349 
350 /**
351  * Overloads of [noCompiledSerializer] should never be called directly.
352  * Instead, compiler inserts calls to them when intrinsifying [serializer] function.
353  *
354  * If no serializer has been found in compile time, call to [noCompiledSerializer] inserted instead.
355  */
356 @Suppress("unused")
357 @PublishedApi
noCompiledSerializernull358 internal fun noCompiledSerializer(forClass: String): KSerializer<*> =
359     throw SerializationException(notRegisteredMessage(forClass))
360 
361 // Used when compiler intrinsic is inserted
362 @OptIn(ExperimentalSerializationApi::class)
363 @Suppress("unused")
364 @PublishedApi
365 internal fun noCompiledSerializer(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
366     return module.getContextual(kClass) ?: kClass.serializerNotRegistered()
367 }
368 
369 @OptIn(ExperimentalSerializationApi::class)
370 @Suppress("unused")
371 @PublishedApi
noCompiledSerializernull372 internal fun noCompiledSerializer(
373     module: SerializersModule,
374     kClass: KClass<*>,
375     argSerializers: Array<KSerializer<*>>
376 ): KSerializer<*> {
377     return module.getContextual(kClass, argSerializers.asList()) ?: kClass.serializerNotRegistered()
378 }
379