1 /*
2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5 package kotlinx.serialization
6
7 import kotlinx.serialization.builtins.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.internal.*
11 import kotlinx.serialization.modules.*
12 import kotlin.reflect.*
13
14 /**
15 * This class provides support for multiplatform polymorphic serialization for interfaces and abstract classes.
16 *
17 * To avoid the most common security pitfalls and reflective lookup (and potential load) of an arbitrary class,
18 * all serializable implementations of any polymorphic type must be [registered][SerializersModuleBuilder.polymorphic]
19 * in advance in the scope of base polymorphic type, efficiently preventing unbounded polymorphic serialization
20 * of an arbitrary type.
21 *
22 * Polymorphic serialization is enabled automatically by default for interfaces and [Serializable] abstract classes.
23 * To enable this feature explicitly on other types, use `@SerializableWith(PolymorphicSerializer::class)`
24 * or [Polymorphic] annotation on the property.
25 *
26 * Usage of the polymorphic serialization can be demonstrated by the following example:
27 * ```
28 * abstract class BaseRequest()
29 * @Serializable
30 * data class RequestA(val id: Int): BaseRequest()
31 * @Serializable
32 * data class RequestB(val s: String): BaseRequest()
33 *
34 * abstract class BaseResponse()
35 * @Serializable
36 * data class ResponseC(val payload: Long): BaseResponse()
37 * @Serializable
38 * data class ResponseD(val payload: ByteArray): BaseResponse()
39 *
40 * @Serializable
41 * data class Message(
42 * @Polymorphic val request: BaseRequest,
43 * @Polymorphic val response: BaseResponse
44 * )
45 * ```
46 * In this example, both request and response in `Message` are serializable with [PolymorphicSerializer].
47 *
48 * `BaseRequest` and `BaseResponse` are base classes and they are captured during compile time by the plugin.
49 * Yet [PolymorphicSerializer] for `BaseRequest` should only allow `RequestA` and `RequestB` serializers, and none of the response's serializers.
50 *
51 * This is achieved via special registration function in the module:
52 * ```
53 * val requestAndResponseModule = SerializersModule {
54 * polymorphic(BaseRequest::class) {
55 * subclass(RequestA::class)
56 * subclass(RequestB::class)
57 * }
58 * polymorphic(BaseResponse::class) {
59 * subclass(ResponseC::class)
60 * subclass(ResponseD::class)
61 * }
62 * }
63 * ```
64 *
65 * @see SerializersModule
66 * @see SerializersModuleBuilder.polymorphic
67 */
68 @OptIn(ExperimentalSerializationApi::class)
69 public class PolymorphicSerializer<T : Any>(override val baseClass: KClass<T>) : AbstractPolymorphicSerializer<T>() {
70
71 @PublishedApi // See comment in SealedClassSerializer
72 internal constructor(
73 baseClass: KClass<T>,
74 classAnnotations: Array<Annotation>
75 ) : this(baseClass) {
76 _annotations = classAnnotations.asList()
77 }
78
79 private var _annotations: List<Annotation> = emptyList()
80
<lambda>null81 public override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) {
82 buildSerialDescriptor("kotlinx.serialization.Polymorphic", PolymorphicKind.OPEN) {
83 element("type", String.serializer().descriptor)
84 element(
85 "value",
86 buildSerialDescriptor("kotlinx.serialization.Polymorphic<${baseClass.simpleName}>", SerialKind.CONTEXTUAL)
87 )
88 annotations = _annotations
89 }.withContext(baseClass)
90 }
91
toStringnull92 override fun toString(): String {
93 return "kotlinx.serialization.PolymorphicSerializer(baseClass: $baseClass)"
94 }
95 }
96
97 @InternalSerializationApi
findPolymorphicSerializernull98 public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
99 decoder: CompositeDecoder,
100 klassName: String?
101 ): DeserializationStrategy<T> =
102 findPolymorphicSerializerOrNull(decoder, klassName) ?: throwSubtypeNotRegistered(klassName, baseClass)
103
104 @InternalSerializationApi
105 public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
106 encoder: Encoder,
107 value: T
108 ): SerializationStrategy<T> =
109 findPolymorphicSerializerOrNull(encoder, value) ?: throwSubtypeNotRegistered(value::class, baseClass)
110