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.descriptors
6
7 import kotlinx.serialization.*
8 import kotlinx.serialization.builtins.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.internal.*
11 import kotlin.reflect.*
12
13 /**
14 * Builder for [SerialDescriptor].
15 * The resulting descriptor will be uniquely identified by the given [serialName], [typeParameters] and
16 * elements structure described in [builderAction] function.
17 *
18 * Example:
19 * ```
20 * // Class with custom serializer and custom serial descriptor
21 * class Data(
22 * val intField: Int, // This field is ignored by custom serializer
23 * val longField: Long, // This field is written as long, but in serialized form is named as "_longField"
24 * val stringList: List<String> // This field is written as regular list of strings
25 * val nullableInt: Int?
26 * )
27 * // Descriptor for such class:
28 * buildClassSerialDescriptor("my.package.Data") {
29 * // intField is deliberately ignored by serializer -- not present in the descriptor as well
30 * element<Long>("_longField") // longField is named as _longField
31 * element("stringField", listSerialDescriptor<String>()) // or ListSerializer(String.serializer()).descriptor
32 * element("nullableInt", serialDescriptor<Int>().nullable)
33 * }
34 * ```
35 *
36 * Example for generic classes:
37 * ```
38 * import kotlinx.serialization.builtins.*
39 *
40 * @Serializable(CustomSerializer::class)
41 * class BoxedList<T>(val list: List<T>)
42 *
43 * class CustomSerializer<T>(tSerializer: KSerializer<T>): KSerializer<BoxedList<T>> {
44 * // here we use tSerializer.descriptor because it represents T
45 * override val descriptor = buildClassSerialDescriptor("pkg.BoxedList", tSerializer.descriptor) {
46 * // here we have to wrap it with List first, because property has type List<T>
47 * element("list", ListSerializer(tSerializer).descriptor) // or listSerialDescriptor(tSerializer.descriptor)
48 * }
49 * }
50 * ```
51 */
52 @Suppress("FunctionName")
53 @OptIn(ExperimentalSerializationApi::class)
54 public fun buildClassSerialDescriptor(
55 serialName: String,
56 vararg typeParameters: SerialDescriptor,
57 builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
58 ): SerialDescriptor {
<lambda>null59 require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
60 val sdBuilder = ClassSerialDescriptorBuilder(serialName)
61 sdBuilder.builderAction()
62 return SerialDescriptorImpl(
63 serialName,
64 StructureKind.CLASS,
65 sdBuilder.elementNames.size,
66 typeParameters.toList(),
67 sdBuilder
68 )
69 }
70
71 /**
72 * Factory to create a trivial primitive descriptors.
73 * Primitive descriptors should be used when the serialized form of the data has a primitive form, for example:
74 * ```
75 * object LongAsStringSerializer : KSerializer<Long> {
76 * override val descriptor: SerialDescriptor =
77 * PrimitiveSerialDescriptor("kotlinx.serialization.LongAsStringSerializer", PrimitiveKind.STRING)
78 *
79 * override fun serialize(encoder: Encoder, value: Long) {
80 * encoder.encodeString(value.toString())
81 * }
82 *
83 * override fun deserialize(decoder: Decoder): Long {
84 * return decoder.decodeString().toLong()
85 * }
86 * }
87 * ```
88 */
PrimitiveSerialDescriptornull89 public fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor {
90 require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
91 return PrimitiveDescriptorSafe(serialName, kind)
92 }
93
94 /**
95 * Factory to create a new descriptor that is identical to [original] except that the name is equal to [serialName].
96 * Should be used when you want to serialize a type as another non-primitive type.
97 * Don't use this if you want to serialize a type as a primitive value, use [PrimitiveSerialDescriptor] instead.
98 *
99 * Example:
100 * ```
101 * @Serializable(CustomSerializer::class)
102 * class CustomType(val a: Int, val b: Int, val c: Int)
103 *
104 * class CustomSerializer: KSerializer<CustomType> {
105 * override val descriptor = SerialDescriptor("CustomType", IntArraySerializer().descriptor)
106 *
107 * override fun serialize(encoder: Encoder, value: CustomType) {
108 * encoder.encodeSerializableValue(IntArraySerializer(), intArrayOf(value.a, value.b, value.c))
109 * }
110 *
111 * override fun deserialize(decoder: Decoder): CustomType {
112 * val array = decoder.decodeSerializableValue(IntArraySerializer())
113 * return CustomType(array[0], array[1], array[2])
114 * }
115 * }
116 * ```
117 */
118 @ExperimentalSerializationApi
SerialDescriptornull119 public fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor {
120 require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
121 require(original.kind !is PrimitiveKind) { "For primitive descriptors please use 'PrimitiveSerialDescriptor' instead" }
122 require(serialName != original.serialName) { "The name of the wrapped descriptor ($serialName) cannot be the same as the name of the original descriptor (${original.serialName})" }
123
124 return WrappedSerialDescriptor(serialName, original)
125 }
126
127 @OptIn(ExperimentalSerializationApi::class)
128 internal class WrappedSerialDescriptor(override val serialName: String, original: SerialDescriptor) : SerialDescriptor by original
129
130 /**
131 * An unsafe alternative to [buildClassSerialDescriptor] that supports an arbitrary [SerialKind].
132 * This function is left public only for migration of pre-release users and is not intended to be used
133 * as generally-safe and stable mechanism. Beware that it can produce inconsistent or non spec-compliant instances.
134 *
135 * If you end up using this builder, please file an issue with your use-case in kotlinx.serialization issue tracker.
136 */
137 @InternalSerializationApi
138 @OptIn(ExperimentalSerializationApi::class)
buildSerialDescriptornull139 public fun buildSerialDescriptor(
140 serialName: String,
141 kind: SerialKind,
142 vararg typeParameters: SerialDescriptor,
143 builder: ClassSerialDescriptorBuilder.() -> Unit = {}
144 ): SerialDescriptor {
<lambda>null145 require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
<lambda>null146 require(kind != StructureKind.CLASS) { "For StructureKind.CLASS please use 'buildClassSerialDescriptor' instead" }
147 val sdBuilder = ClassSerialDescriptorBuilder(serialName)
148 sdBuilder.builder()
149 return SerialDescriptorImpl(serialName, kind, sdBuilder.elementNames.size, typeParameters.toList(), sdBuilder)
150 }
151
152
153 /**
154 * Retrieves descriptor of type [T] using reified [serializer] function.
155 */
serialDescriptornull156 public inline fun <reified T> serialDescriptor(): SerialDescriptor = serializer<T>().descriptor
157
158 /**
159 * Retrieves descriptor of type associated with the given [KType][type]
160 */
161 public fun serialDescriptor(type: KType): SerialDescriptor = serializer(type).descriptor
162
163 /**
164 * Creates a descriptor for the type `List<T>` where `T` is the type associated with [elementDescriptor].
165 */
166 @ExperimentalSerializationApi
167 public fun listSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
168 return ArrayListClassDesc(elementDescriptor)
169 }
170
171 /**
172 * Creates a descriptor for the type `List<T>`.
173 */
174 @ExperimentalSerializationApi
listSerialDescriptornull175 public inline fun <reified T> listSerialDescriptor(): SerialDescriptor {
176 return listSerialDescriptor(serializer<T>().descriptor)
177 }
178
179 /**
180 * Creates a descriptor for the type `Map<K, V>` where `K` and `V` are types
181 * associated with [keyDescriptor] and [valueDescriptor] respectively.
182 */
183 @ExperimentalSerializationApi
mapSerialDescriptornull184 public fun mapSerialDescriptor(
185 keyDescriptor: SerialDescriptor,
186 valueDescriptor: SerialDescriptor
187 ): SerialDescriptor {
188 return HashMapClassDesc(keyDescriptor, valueDescriptor)
189 }
190
191 /**
192 * Creates a descriptor for the type `Map<K, V>`.
193 */
194 @ExperimentalSerializationApi
mapSerialDescriptornull195 public inline fun <reified K, reified V> mapSerialDescriptor(): SerialDescriptor {
196 return mapSerialDescriptor(serializer<K>().descriptor, serializer<V>().descriptor)
197 }
198
199 /**
200 * Creates a descriptor for the type `Set<T>` where `T` is the type associated with [elementDescriptor].
201 */
202 @ExperimentalSerializationApi
setSerialDescriptornull203 public fun setSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
204 return HashSetClassDesc(elementDescriptor)
205 }
206
207 /**
208 * Creates a descriptor for the type `Set<T>`.
209 */
210 @ExperimentalSerializationApi
setSerialDescriptornull211 public inline fun <reified T> setSerialDescriptor(): SerialDescriptor {
212 return setSerialDescriptor(serializer<T>().descriptor)
213 }
214
215 /**
216 * Returns new serial descriptor for the same type with [isNullable][SerialDescriptor.isNullable]
217 * property set to `true`.
218 */
219 @OptIn(ExperimentalSerializationApi::class)
220 public val SerialDescriptor.nullable: SerialDescriptor
221 get() {
222 if (this.isNullable) return this
223 return SerialDescriptorForNullable(this)
224 }
225
226 /**
227 * Builder for [SerialDescriptor] for user-defined serializers.
228 *
229 * Both explicit builder functions and implicit (using reified type-parameters) are present and are equivalent.
230 * For example, `element<Int?>("nullableIntField")` is indistinguishable from
231 * `element("nullableIntField", IntSerializer.descriptor.nullable)` and
232 * from `element("nullableIntField", descriptor<Int?>)`.
233 *
234 * Please refer to [SerialDescriptor] builder function for a complete example.
235 */
236 public class ClassSerialDescriptorBuilder internal constructor(
237 public val serialName: String
238 ) {
239
240 /**
241 * Indicates that serializer associated with the current serial descriptor
242 * support nullable types, meaning that it should declare nullable type
243 * in its [KSerializer] type parameter and handle nulls during encoding and decoding.
244 */
245 @ExperimentalSerializationApi
246 @Deprecated("isNullable inside buildSerialDescriptor is deprecated. Please use SerialDescriptor.nullable extension on a builder result.", level = DeprecationLevel.ERROR)
247 public var isNullable: Boolean = false
248
249 /**
250 * [Serial][SerialInfo] annotations on a target type.
251 */
252 @ExperimentalSerializationApi
253 public var annotations: List<Annotation> = emptyList()
254
255 internal val elementNames: MutableList<String> = ArrayList()
256 private val uniqueNames: MutableSet<String> = HashSet()
257 internal val elementDescriptors: MutableList<SerialDescriptor> = ArrayList()
258 internal val elementAnnotations: MutableList<List<Annotation>> = ArrayList()
259 internal val elementOptionality: MutableList<Boolean> = ArrayList()
260
261 /**
262 * Add an element with a given [name][elementName], [descriptor],
263 * type annotations and optionality the resulting descriptor.
264 *
265 * Example of usage:
266 * ```
267 * class Data(
268 * val intField: Int? = null, // Optional, has default value
269 * @ProtoNumber(1) val longField: Long
270 * )
271 *
272 * // Corresponding descriptor
273 * SerialDescriptor("package.Data") {
274 * element<Int?>("intField", isOptional = true)
275 * element<Long>("longField", annotations = listOf(protoIdAnnotationInstance))
276 * }
277 * ```
278 */
elementnull279 public fun element(
280 elementName: String,
281 descriptor: SerialDescriptor,
282 annotations: List<Annotation> = emptyList(),
283 isOptional: Boolean = false
284 ) {
285 require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered in $serialName" }
286 elementNames += elementName
287 elementDescriptors += descriptor
288 elementAnnotations += annotations
289 elementOptionality += isOptional
290 }
291 }
292
293 /**
294 * A reified version of [element] function that
295 * extract descriptor using `serializer<T>().descriptor` call with all the restrictions of `serializer<T>().descriptor`.
296 */
elementnull297 public inline fun <reified T> ClassSerialDescriptorBuilder.element(
298 elementName: String,
299 annotations: List<Annotation> = emptyList(),
300 isOptional: Boolean = false
301 ) {
302 val descriptor = serializer<T>().descriptor
303 element(elementName, descriptor, annotations, isOptional)
304 }
305
306 @OptIn(ExperimentalSerializationApi::class)
307 internal class SerialDescriptorImpl(
308 override val serialName: String,
309 override val kind: SerialKind,
310 override val elementsCount: Int,
311 typeParameters: List<SerialDescriptor>,
312 builder: ClassSerialDescriptorBuilder
313 ) : SerialDescriptor, CachedNames {
314
315 override val annotations: List<Annotation> = builder.annotations
316 override val serialNames: Set<String> = builder.elementNames.toHashSet()
317
318 private val elementNames: Array<String> = builder.elementNames.toTypedArray()
319 private val elementDescriptors: Array<SerialDescriptor> = builder.elementDescriptors.compactArray()
320 private val elementAnnotations: Array<List<Annotation>> = builder.elementAnnotations.toTypedArray()
321 private val elementOptionality: BooleanArray = builder.elementOptionality.toBooleanArray()
<lambda>null322 private val name2Index: Map<String, Int> = elementNames.withIndex().map { it.value to it.index }.toMap()
323 private val typeParametersDescriptors: Array<SerialDescriptor> = typeParameters.compactArray()
<lambda>null324 private val _hashCode: Int by lazy { hashCodeImpl(typeParametersDescriptors) }
325
getElementNamenull326 override fun getElementName(index: Int): String = elementNames.getChecked(index)
327 override fun getElementIndex(name: String): Int = name2Index[name] ?: CompositeDecoder.UNKNOWN_NAME
328 override fun getElementAnnotations(index: Int): List<Annotation> = elementAnnotations.getChecked(index)
329 override fun getElementDescriptor(index: Int): SerialDescriptor = elementDescriptors.getChecked(index)
330 override fun isElementOptional(index: Int): Boolean = elementOptionality.getChecked(index)
331
332 override fun equals(other: Any?): Boolean =
333 equalsImpl(other) { otherDescriptor: SerialDescriptorImpl ->
334 typeParametersDescriptors.contentEquals(
335 otherDescriptor.typeParametersDescriptors
336 )
337 }
338
hashCodenull339 override fun hashCode(): Int = _hashCode
340
341 override fun toString(): String {
342 return (0 until elementsCount).joinToString(", ", prefix = "$serialName(", postfix = ")") {
343 getElementName(it) + ": " + getElementDescriptor(it).serialName
344 }
345 }
346 }
347
348