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