1 /* 2 * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.serialization.descriptors 6 7 import kotlinx.serialization.* 8 import kotlinx.serialization.builtins.* 9 import kotlinx.serialization.encoding.* 10 11 /** 12 * Serial descriptor is an inherent property of [KSerializer] that describes the structure of the serializable type. 13 * The structure of the serializable type is not only the characteristic of the type itself, but also of the serializer as well, 14 * meaning that one type can have multiple descriptors that have completely different structure. 15 * 16 * For example, the class `class Color(val rgb: Int)` can have multiple serializable representations, 17 * such as `{"rgb": 255}`, `"#0000FF"`, `[0, 0, 255]` and `{"red": 0, "green": 0, "blue": 255}`. 18 * Representations are determined by serializers and each such serializer has its own descriptor that identifies 19 * each structure in a distinguishable and format-agnostic manner. 20 * 21 * ### Structure 22 * Serial descriptor is identified by its [name][serialName] and consists of kind, potentially empty set of 23 * children elements and additional metadata. 24 * 25 * * [serialName] uniquely identifies the descriptor (and the corresponding serializer) for non-generic types. 26 * For generic types, the actual type substitution is omitted from the string representation and the name 27 * identifies the family of the serializers without type substitutions. However, type substitution is accounted 28 * in [equals] and [hashCode] operations, meaning that descriptors of generic classes with the same name, but different type 29 * arguments, are not equal to each other. 30 * [serialName] is typically used to specify the type of the target class during serialization of polymorphic and sealed 31 * classes, for observability and diagnostics. 32 * * [Kind][SerialKind] defines what this descriptor represents: primitive, enum, object, collection etc. 33 * * Children elements are represented as serial descriptors as well and define the structure of the type's elements. 34 * * Metadata carries additional information, such as [nullability][nullable], [optionality][isElementOptional] 35 * and [serial annotations][getElementAnnotations]. 36 * 37 * ### Usages 38 * There are two general usages of the descriptors: THE serialization process and serialization introspection. 39 * 40 * #### Serialization 41 * Serial descriptor is used as a bridge between decoders/encoders and serializers. 42 * When asking for a next element, the serializer provides an expected descriptor to the decoder, and, 43 * based on the descriptor content, decoder decides how to parse its input. 44 * In JSON, for example, when the encoder is asked to encode the next element and this element 45 * is a subtype of [List], the encoder receives a descriptor with [StructureKind.LIST] and, based on that, 46 * first writes an opening square bracket before writing the content of the list. 47 * 48 * Serial descriptor _encapsulates_ the structure of the data, so serializers can be free from 49 * format-specific details. `ListSerializer` knows nothing about JSON and square brackets, providing 50 * only the structure of the data and delegating encoding decision to the format itself. 51 * 52 * #### Introspection 53 * Another usage of a serial descriptor is type introspection without its serialization. 54 * Introspection can be used to check, whether the given serializable class complies the 55 * corresponding scheme and to generate JSON or ProtoBuf schema from the given class. 56 * 57 * ### Indices 58 * Serial descriptor API operates with children indices. 59 * For the fixed-size structures, such as regular classes, index is represented by a value in 60 * the range from zero to [elementsCount] and represent and index of the property in this class. 61 * Consequently, primitives do not have children and their element count is zero. 62 * 63 * For collections and maps indices don't have fixed bound. Regular collections descriptors usually 64 * have one element (`T`, maps have two, one for keys and one for values), but potentially unlimited 65 * number of actual children values. Valid indices range is not known statically 66 * and implementations of such descriptor should provide consistent and unbounded names and indices. 67 * 68 * In practice, for regular classes it is allowed to invoke `getElement*(index)` methods 69 * with an index from `0` to [elementsCount] range and element at the particular index corresponds to the 70 * serializable property at the given position. 71 * For collections and maps, index parameter for `getElement*(index)` methods is effectively bounded 72 * by the maximal number of collection/map elements. 73 * 74 * ### Thread-safety and mutability 75 * Serial descriptor implementation should be immutable and, thus, thread-safe. 76 * 77 * ### Equality and caching 78 * Serial descriptor can be used as a unique identifier for format-specific data or schemas and 79 * this implies the following restrictions on its `equals` and `hashCode`: 80 * 81 * An [equals] implementation should use both [serialName] and elements structure. 82 * Comparing [elementDescriptors] directly is discouraged, 83 * because it may cause a stack overflow error, e.g. if a serializable class `T` contains elements of type `T`. 84 * To avoid it, a serial descriptor implementation should compare only descriptors 85 * of class' type parameters, in a way that `serializer<Box<Int>>().descriptor != serializer<Box<String>>().descriptor`. 86 * If type parameters are equal, descriptors structure should be compared by using children elements 87 * descriptors' [serialName]s, which correspond to class names 88 * (do not confuse with elements own names, which correspond to properties names); and/or other [SerialDescriptor] 89 * properties, such as [kind]. 90 * An example of [equals] implementation: 91 * ``` 92 * if (this === other) return true 93 * if (other::class != this::class) return false 94 * if (serialName != other.serialName) return false 95 * if (!typeParametersAreEqual(other)) return false 96 * if (this.elementDescriptors().map { it.serialName } != other.elementDescriptors().map { it.serialName }) return false 97 * return true 98 * ``` 99 * 100 * [hashCode] implementation should use the same properties for computing the result. 101 * 102 * ### User-defined serial descriptors 103 * The best way to define a custom descriptor is to use [buildClassSerialDescriptor] builder function, where 104 * for each serializable property the corresponding element is declared. 105 * 106 * Example: 107 * ``` 108 * // Class with custom serializer and custom serial descriptor 109 * class Data( 110 * val intField: Int, // This field is ignored by custom serializer 111 * val longField: Long, // This field is written as long, but in serialized form is named as "_longField" 112 * val stringList: List<String> // This field is written as regular list of strings 113 * ) 114 * 115 * // Descriptor for such class: 116 * buildClassSerialDescriptor("my.package.Data") { 117 * // intField is deliberately ignored by serializer -- not present in the descriptor as well 118 * element<Long>("_longField") // longField is named as _longField 119 * element("stringField", listSerialDescriptor<String>()) 120 * } 121 * 122 * // Example of 'serialize' function for such descriptor 123 * override fun serialize(encoder: Encoder, value: Data) { 124 * encoder.encodeStructure(descriptor) { 125 * encodeLongElement(descriptor, 0, value.longField) // Will be written as "_longField" because descriptor's child at index 0 says so 126 * encodeSerializableElement(descriptor, 1, ListSerializer(String.serializer()), value.stringList) 127 * } 128 * } 129 * ``` 130 * 131 * For a classes that are represented as a single primitive value, [PrimitiveSerialDescriptor] builder function can be used instead. 132 * 133 * ### Consistency violations 134 * An implementation of [SerialDescriptor] should be consistent with the implementation of the corresponding [KSerializer]. 135 * Yet it is not type-checked statically, thus making it possible to declare a non-consistent implementations of descriptor and serializer. 136 * In such cases, the behaviour of an underlying format is unspecified and may lead to both runtime errors and encoding of 137 * corrupted data that is impossible to decode back. 138 * 139 * ### Not stable for inheritance 140 * 141 * `SerialDescriptor` interface is not stable for inheritance in 3rd party libraries, as new methods 142 * might be added to this interface or contracts of the existing methods can be changed. 143 * This interface is safe to build using [buildClassSerialDescriptor] and [PrimitiveSerialDescriptor], 144 * and is safe to delegate implementation to existing instances. 145 */ 146 public interface SerialDescriptor { 147 /** 148 * Serial name of the descriptor that identifies pair of the associated serializer and target class. 149 * 150 * For generated serializers, serial name is equal to the corresponding class's fully-qualified name 151 * or, if overridden, [SerialName]. 152 * Custom serializers should provide a unique serial name that identify both the serializable class and 153 * the serializer itself, ignoring type arguments, if they are present. 154 */ 155 @ExperimentalSerializationApi 156 public val serialName: String 157 158 /** 159 * The kind of the serialized form that determines **the shape** of the serialized data. 160 * Formats use serial kind to add and parse serializer-agnostic metadata to the result. 161 * 162 * For example, JSON format wraps [classes][StructureKind.CLASS] and [StructureKind.MAP] into 163 * brackets, while ProtoBuf just serialize these types in separate ways. 164 * 165 * Kind should be consistent with the implementation, for example, if it is a [primitive][PrimitiveKind], 166 * then its elements count should be zero and vice versa. 167 */ 168 @ExperimentalSerializationApi 169 public val kind: SerialKind 170 171 /** 172 * Whether the descriptor describes nullable element. 173 * Returns `true` if associated serializer can serialize/deserialize nullable elements of the described type. 174 */ 175 @ExperimentalSerializationApi 176 public val isNullable: Boolean get() = false 177 178 /** 179 * Returns `true` if this descriptor describes a serializable value class which underlying value 180 * is serialized directly. 181 */ 182 public val isInline: Boolean get() = false 183 184 /** 185 * The number of elements this descriptor describes, besides from the class itself. 186 * [elementsCount] describes the number of **semantic** elements, not the number 187 * of actual fields/properties in the serialized form, even though they frequently match. 188 * 189 * For example, for the following class 190 * `class Complex(val real: Long, val imaginary: Long)` the corresponding descriptor 191 * and the serialized form both have two elements, while for `class IntList : ArrayList<Int>()` 192 * the corresponding descriptor has a single element (`IntDescriptor`, the type of list element), 193 * but from zero up to `Int.MAX_VALUE` values in the serialized form. 194 */ 195 @ExperimentalSerializationApi 196 public val elementsCount: Int 197 198 /** 199 * Returns serial annotations of the associated class. 200 * Serial annotations can be used to specify an additional metadata that may be used during serialization. 201 * Only annotations marked with [SerialInfo] are added to the resulting list. 202 */ 203 @ExperimentalSerializationApi 204 public val annotations: List<Annotation> get() = emptyList() 205 206 /** 207 * Returns a positional name of the child at the given [index]. 208 * Positional name represents a corresponding property name in the class, associated with 209 * the current descriptor. 210 * 211 * @throws IndexOutOfBoundsException for an illegal [index] values. 212 * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive) 213 */ 214 @ExperimentalSerializationApi getElementNamenull215 public fun getElementName(index: Int): String 216 217 /** 218 * Returns an index in the children list of the given element by its name or [CompositeDecoder.UNKNOWN_NAME] 219 * if there is no such element. 220 * The resulting index, if it is not [CompositeDecoder.UNKNOWN_NAME], is guaranteed to be usable with [getElementName]. 221 */ 222 @ExperimentalSerializationApi 223 public fun getElementIndex(name: String): Int 224 225 /** 226 * Returns serial annotations of the child element at the given [index]. 227 * This method differs from `getElementDescriptor(index).annotations` by reporting only 228 * declaration-specific annotations: 229 * ``` 230 * @Serializable 231 * @SomeSerialAnnotation 232 * class Nested(...) 233 * 234 * @Serializable 235 * class Outer(@AnotherSerialAnnotation val nested: Nested) 236 * 237 * outerDescriptor.getElementAnnotations(0) // Returns [@AnotherSerialAnnotation] 238 * outerDescriptor.getElementDescriptor(0).annotations // Returns [@SomeSerialAnnotation] 239 * ``` 240 * Only annotations marked with [SerialInfo] are added to the resulting list. 241 * 242 * @throws IndexOutOfBoundsException for an illegal [index] values. 243 * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive). 244 */ 245 @ExperimentalSerializationApi 246 public fun getElementAnnotations(index: Int): List<Annotation> 247 248 /** 249 * Retrieves the descriptor of the child element for the given [index]. 250 * For the property of type `T` on the position `i`, `getElementDescriptor(i)` yields the same result 251 * as for `T.serializer().descriptor`, if the serializer for this property is not explicitly overridden 252 * with `@Serializable(with = ...`)`, [Polymorphic] or [Contextual]. 253 * This method can be used to completely introspect the type that the current descriptor describes. 254 * 255 * @throws IndexOutOfBoundsException for illegal [index] values. 256 * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive). 257 */ 258 @ExperimentalSerializationApi 259 public fun getElementDescriptor(index: Int): SerialDescriptor 260 261 /** 262 * Whether the element at the given [index] is optional (can be absent in serialized form). 263 * For generated descriptors, all elements that have a corresponding default parameter value are 264 * marked as optional. Custom serializers can treat optional values in a serialization-specific manner 265 * without default parameters constraint. 266 * 267 * Example of optionality: 268 * ``` 269 * @Serializable 270 * class Holder( 271 * val a: Int, // Optional == false 272 * val b: Int?, // Optional == false 273 * val c: Int? = null, // Optional == true 274 * val d: List<Int>, // Optional == false 275 * val e: List<Int> = listOf(1), // Optional == true 276 * ) 277 * ``` 278 * Returns `false` for valid indices of collections, maps and enums. 279 * 280 * @throws IndexOutOfBoundsException for an illegal [index] values. 281 * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive). 282 */ 283 @ExperimentalSerializationApi 284 public fun isElementOptional(index: Int): Boolean 285 } 286 287 /** 288 * Returns an iterable of all descriptor [elements][SerialDescriptor.getElementDescriptor]. 289 */ 290 @ExperimentalSerializationApi 291 public val SerialDescriptor.elementDescriptors: Iterable<SerialDescriptor> 292 get() = Iterable { 293 object : Iterator<SerialDescriptor> { 294 private var elementsLeft = elementsCount 295 override fun hasNext(): Boolean = elementsLeft > 0 296 297 override fun next(): SerialDescriptor { 298 return getElementDescriptor(elementsCount - (elementsLeft--)) 299 } 300 } 301 } 302 303 /** 304 * Returns an iterable of all descriptor [element names][SerialDescriptor.getElementName]. 305 */ 306 @ExperimentalSerializationApi 307 public val SerialDescriptor.elementNames: Iterable<String> <lambda>null308 get() = Iterable { 309 object : Iterator<String> { 310 private var elementsLeft = elementsCount 311 override fun hasNext(): Boolean = elementsLeft > 0 312 313 override fun next(): String { 314 return getElementName(elementsCount - (elementsLeft--)) 315 } 316 } 317 } 318