1 /* <lambda>null2 * 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 of sealed classes. 16 * 17 * In contrary to [PolymorphicSerializer], all known subclasses with serializers must be passed 18 * in `subclasses` and `subSerializers` constructor parameters. 19 * If a subclass is a sealed class itself, all its subclasses are registered as well. 20 * 21 * If a sealed hierarchy is marked with [@Serializable][Serializable], an instance of this class is provided automatically. 22 * In most of the cases, you won't need to perform any manual setup: 23 * 24 * ``` 25 * @Serializable 26 * sealed class SimpleSealed { 27 * @Serializable 28 * public data class SubSealedA(val s: String) : SimpleSealed() 29 * 30 * @Serializable 31 * public data class SubSealedB(val i: Int) : SimpleSealed() 32 * } 33 * 34 * // will perform correct polymorphic serialization and deserialization: 35 * Json.encodeToString(SimpleSealed.serializer(), SubSealedA("foo")) 36 * ``` 37 * 38 * However, it is possible to register additional subclasses using regular [SerializersModule]. 39 * It is required when one of the subclasses is an abstract class itself: 40 * 41 * ``` 42 * @Serializable 43 * sealed class ProtocolWithAbstractClass { 44 * @Serializable 45 * abstract class Message : ProtocolWithAbstractClass() { 46 * @Serializable 47 * data class StringMessage(val description: String, val message: String) : Message() 48 * 49 * @Serializable 50 * data class IntMessage(val description: String, val message: Int) : Message() 51 * } 52 * 53 * @Serializable 54 * data class ErrorMessage(val error: String) : ProtocolWithAbstractClass() 55 * } 56 * ``` 57 * 58 * In this case, `ErrorMessage` would be registered automatically by the plugin, 59 * but `StringMessage` and `IntMessage` require manual registration, as described in [PolymorphicSerializer] documentation: 60 * 61 * ``` 62 * val abstractContext = SerializersModule { 63 * polymorphic(ProtocolWithAbstractClass::class) { 64 * subclass(ProtocolWithAbstractClass.Message.IntMessage::class) 65 * subclass(ProtocolWithAbstractClass.Message.StringMessage::class) 66 * // no need to register ProtocolWithAbstractClass.ErrorMessage 67 * } 68 * } 69 * ``` 70 */ 71 @InternalSerializationApi 72 @OptIn(ExperimentalSerializationApi::class) 73 public class SealedClassSerializer<T : Any>( 74 serialName: String, 75 override val baseClass: KClass<T>, 76 subclasses: Array<KClass<out T>>, 77 subclassSerializers: Array<KSerializer<out T>> 78 ) : AbstractPolymorphicSerializer<T>() { 79 80 /** 81 * This constructor is needed to store serial info annotations defined on the sealed class. 82 * Support for such annotations was added in Kotlin 1.5.30; previous plugins used primary constructor of this class 83 * directly, therefore this constructor is secondary. 84 * 85 * This constructor can (and should) became primary when Require-Kotlin-Version is raised to at least 1.5.30 86 * to remove necessity to store annotations separately and calculate descriptor via `lazy {}`. 87 * 88 * When doing this change, also migrate secondary constructors from [PolymorphicSerializer] and [ObjectSerializer]. 89 */ 90 @PublishedApi 91 internal constructor( 92 serialName: String, 93 baseClass: KClass<T>, 94 subclasses: Array<KClass<out T>>, 95 subclassSerializers: Array<KSerializer<out T>>, 96 classAnnotations: Array<Annotation> 97 ) : this(serialName, baseClass, subclasses, subclassSerializers) { 98 this._annotations = classAnnotations.asList() 99 } 100 101 private var _annotations: List<Annotation> = emptyList() 102 103 override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) { 104 buildSerialDescriptor(serialName, PolymorphicKind.SEALED) { 105 element("type", String.serializer().descriptor) 106 val elementDescriptor = 107 buildSerialDescriptor("kotlinx.serialization.Sealed<${baseClass.simpleName}>", SerialKind.CONTEXTUAL) { 108 // serialName2Serializer is guaranteed to have no duplicates — checked in `init`. 109 serialName2Serializer.forEach { (name, serializer) -> 110 element(name, serializer.descriptor) 111 } 112 } 113 element("value", elementDescriptor) 114 annotations = _annotations 115 } 116 } 117 118 private val class2Serializer: Map<KClass<out T>, KSerializer<out T>> 119 private val serialName2Serializer: Map<String, KSerializer<out T>> 120 121 init { 122 if (subclasses.size != subclassSerializers.size) { 123 throw IllegalArgumentException("All subclasses of sealed class ${baseClass.simpleName} should be marked @Serializable") 124 } 125 126 // Note: we do not check whether different serializers are provided if the same KClass duplicated in the `subclasses`. 127 // Plugin should produce identical serializers, although they are not always strictly equal (e.g. new ObjectSerializer 128 // may be created every time) 129 class2Serializer = subclasses.zip(subclassSerializers).toMap() 130 serialName2Serializer = class2Serializer.entries.groupingBy { it.value.descriptor.serialName } 131 .aggregate<Map.Entry<KClass<out T>, KSerializer<out T>>, String, Map.Entry<KClass<*>, KSerializer<out T>>> 132 { key, accumulator, element, _ -> 133 if (accumulator != null) { 134 error( 135 "Multiple sealed subclasses of '$baseClass' have the same serial name '$key':" + 136 " '${accumulator.key}', '${element.key}'" 137 ) 138 } 139 element 140 }.mapValues { it.value.value } 141 } 142 143 override fun findPolymorphicSerializerOrNull( 144 decoder: CompositeDecoder, 145 klassName: String? 146 ): DeserializationStrategy<T>? { 147 return serialName2Serializer[klassName] ?: super.findPolymorphicSerializerOrNull(decoder, klassName) 148 } 149 150 override fun findPolymorphicSerializerOrNull(encoder: Encoder, value: T): SerializationStrategy<T>? { 151 return (class2Serializer[value::class] ?: super.findPolymorphicSerializerOrNull(encoder, value))?.cast() 152 } 153 } 154