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