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