0
# Descriptors
1
2
The descriptor system in kotlinx.serialization provides structural metadata about serializable types. Descriptors describe the shape, names, and characteristics of serialized data, enabling format implementations to understand how to encode and decode objects without needing to know the actual Kotlin types.
3
4
## Capabilities
5
6
### Core Descriptor Interface
7
8
The fundamental interface that describes the structure of any serializable type.
9
10
```kotlin { .api }
11
/**
12
* Describes the structure of a serializable type including its name, kind, and elements.
13
* Used by encoders and decoders to understand the shape of data they're processing.
14
*/
15
interface SerialDescriptor {
16
/** The serial name of the described type */
17
val serialName: String
18
19
/** The kind of serial data (primitive, structured, etc.) */
20
val kind: SerialKind
21
22
/** Number of elements in this descriptor */
23
val elementsCount: Int
24
25
/** Annotations present on the described type */
26
val annotations: List<Annotation>
27
28
/** Whether this descriptor represents an inline value class */
29
val isInline: Boolean
30
31
/** Whether this descriptor is nullable */
32
val isNullable: Boolean
33
34
/** Get the name of element at specified index */
35
fun getElementName(index: Int): String
36
37
/** Get the index of element with specified name */
38
fun getElementIndex(name: String): Int
39
40
/** Get annotations for element at specified index */
41
fun getElementAnnotations(index: Int): List<Annotation>
42
43
/** Get descriptor for element at specified index */
44
fun getElementDescriptor(index: Int): SerialDescriptor
45
46
/** Check if element at specified index is optional */
47
fun isElementOptional(index: Int): Boolean
48
}
49
```
50
51
**Usage Examples:**
52
53
```kotlin
54
import kotlinx.serialization.*
55
import kotlinx.serialization.descriptors.*
56
57
@Serializable
58
data class User(val name: String, val age: Int, val email: String? = null)
59
60
val descriptor = User.serializer().descriptor
61
62
println(descriptor.serialName) // "User"
63
println(descriptor.kind) // StructureKind.CLASS
64
println(descriptor.elementsCount) // 3
65
66
// Inspect elements
67
for (i in 0 until descriptor.elementsCount) {
68
println("${descriptor.getElementName(i)}: ${descriptor.isElementOptional(i)}")
69
}
70
// name: false
71
// age: false
72
// email: true
73
```
74
75
### Serial Kinds
76
77
Type hierarchy that categorizes different kinds of serializable data structures.
78
79
```kotlin { .api }
80
/**
81
* Base sealed class for all serial kinds that categorize serializable types.
82
*/
83
sealed class SerialKind
84
85
/**
86
* Kinds for primitive values that can be directly encoded/decoded.
87
*/
88
sealed class PrimitiveKind : SerialKind() {
89
object BOOLEAN : PrimitiveKind()
90
object BYTE : PrimitiveKind()
91
object CHAR : PrimitiveKind()
92
object SHORT : PrimitiveKind()
93
object INT : PrimitiveKind()
94
object LONG : PrimitiveKind()
95
object FLOAT : PrimitiveKind()
96
object DOUBLE : PrimitiveKind()
97
object STRING : PrimitiveKind()
98
}
99
100
/**
101
* Kinds for structured values that contain multiple elements.
102
*/
103
sealed class StructureKind : SerialKind() {
104
/** Regular class with named properties */
105
object CLASS : StructureKind()
106
107
/** Ordered collection of elements */
108
object LIST : StructureKind()
109
110
/** Key-value mapping */
111
object MAP : StructureKind()
112
113
/** Object instance (singleton) */
114
object OBJECT : StructureKind()
115
}
116
117
/**
118
* Kinds for polymorphic types that can have multiple runtime implementations.
119
*/
120
sealed class PolymorphicKind : SerialKind() {
121
/** Sealed class hierarchy with known subclasses */
122
object SEALED : PolymorphicKind()
123
124
/** Open class/interface with potentially unknown subclasses */
125
object OPEN : PolymorphicKind()
126
}
127
128
/**
129
* Special kinds for specific serialization scenarios.
130
*/
131
object SerialKind {
132
/** Type that requires contextual serializer lookup */
133
object CONTEXTUAL : SerialKind()
134
135
/** Enum type */
136
object ENUM : SerialKind()
137
}
138
```
139
140
### Descriptor Factory Functions
141
142
Factory functions for creating descriptors programmatically.
143
144
```kotlin { .api }
145
/**
146
* Creates a primitive descriptor for basic types.
147
* @param serialName The name of the described type
148
* @param kind The primitive kind
149
* @return SerialDescriptor for the primitive type
150
*/
151
fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor
152
153
/**
154
* DSL builder for creating class descriptors with elements.
155
* @param serialName The name of the described type
156
* @param builderAction Lambda to configure descriptor elements
157
* @return SerialDescriptor for the class
158
*/
159
fun buildClassSerialDescriptor(
160
serialName: String,
161
builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
162
): SerialDescriptor
163
164
/**
165
* Creates a descriptor identical to original but with different name.
166
* @param serialName The new name for the descriptor
167
* @param original The original descriptor to copy
168
* @return SerialDescriptor with new name
169
*/
170
fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor
171
172
/**
173
* Builder class for constructing class descriptors.
174
*/
175
class ClassSerialDescriptorBuilder {
176
/** Add an element to the descriptor */
177
fun element(
178
elementName: String,
179
descriptor: SerialDescriptor,
180
annotations: List<Annotation> = emptyList(),
181
isOptional: Boolean = false
182
)
183
184
/** Add annotations to the descriptor itself */
185
fun annotations(annotations: List<Annotation>)
186
}
187
```
188
189
**Usage Examples:**
190
191
```kotlin
192
import kotlinx.serialization.descriptors.*
193
194
// Create primitive descriptor
195
val stringDescriptor = PrimitiveSerialDescriptor("MyString", PrimitiveKind.STRING)
196
197
// Create class descriptor with DSL
198
val personDescriptor = buildClassSerialDescriptor("Person") {
199
element("name", PrimitiveSerialDescriptor("kotlin.String", PrimitiveKind.STRING))
200
element("age", PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT))
201
element("email", PrimitiveSerialDescriptor("kotlin.String", PrimitiveKind.STRING), isOptional = true)
202
}
203
204
// Copy descriptor with new name
205
val aliasDescriptor = SerialDescriptor("PersonAlias", personDescriptor)
206
```
207
208
### Collection Descriptor Functions
209
210
Specialized functions for creating descriptors for collection types.
211
212
```kotlin { .api }
213
/**
214
* Creates a descriptor for List<T> structures.
215
* @param elementDescriptor Descriptor for list elements
216
* @return SerialDescriptor for List type
217
*/
218
fun listSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor
219
220
/**
221
* Creates a descriptor for Set<T> structures.
222
* @param elementDescriptor Descriptor for set elements
223
* @return SerialDescriptor for Set type
224
*/
225
fun setSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor
226
227
/**
228
* Creates a descriptor for Map<K,V> structures.
229
* @param keyDescriptor Descriptor for map keys
230
* @param valueDescriptor Descriptor for map values
231
* @return SerialDescriptor for Map type
232
*/
233
fun mapSerialDescriptor(
234
keyDescriptor: SerialDescriptor,
235
valueDescriptor: SerialDescriptor
236
): SerialDescriptor
237
```
238
239
### Descriptor Lookup Functions
240
241
Functions for obtaining descriptors at runtime.
242
243
```kotlin { .api }
244
/**
245
* Retrieves a descriptor for the given reified type T.
246
* @return SerialDescriptor for type T
247
*/
248
inline fun <reified T> serialDescriptor(): SerialDescriptor
249
250
/**
251
* Retrieves a descriptor for the given KType.
252
* @param type The KType to get descriptor for
253
* @return SerialDescriptor for the specified type
254
*/
255
fun serialDescriptor(type: KType): SerialDescriptor
256
```
257
258
**Usage Examples:**
259
260
```kotlin
261
import kotlinx.serialization.*
262
import kotlinx.serialization.descriptors.*
263
import kotlin.reflect.typeOf
264
265
@Serializable
266
data class Product(val name: String, val price: Double)
267
268
// Get descriptor for known type
269
val productDescriptor = serialDescriptor<Product>()
270
271
// Get descriptor for collection types
272
val listDescriptor = serialDescriptor<List<Product>>()
273
val mapDescriptor = serialDescriptor<Map<String, Product>>()
274
275
// Runtime descriptor lookup
276
val typeDescriptor = serialDescriptor(typeOf<List<String>>())
277
278
// Create collection descriptors manually
279
val customListDesc = listSerialDescriptor(PrimitiveSerialDescriptor("kotlin.String", PrimitiveKind.STRING))
280
val customMapDesc = mapSerialDescriptor(
281
PrimitiveSerialDescriptor("kotlin.String", PrimitiveKind.STRING),
282
serialDescriptor<Product>()
283
)
284
```
285
286
### Descriptor Extension Properties
287
288
Additional properties available on descriptors for advanced use cases.
289
290
```kotlin { .api }
291
/**
292
* Extension property that returns the captured KClass for the descriptor.
293
* Useful for runtime type checking and reflection operations.
294
*/
295
val SerialDescriptor.capturedKClass: KClass<Any>?
296
```
297
298
## Usage Patterns
299
300
### Custom Serializer Descriptors
301
302
When implementing custom serializers, you typically need to provide appropriate descriptors:
303
304
```kotlin
305
import kotlinx.serialization.*
306
import kotlinx.serialization.descriptors.*
307
import kotlinx.serialization.encoding.*
308
309
object LocalDateSerializer : KSerializer<LocalDate> {
310
override val descriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING)
311
312
override fun serialize(encoder: Encoder, value: LocalDate) {
313
encoder.encodeString(value.toString())
314
}
315
316
override fun deserialize(decoder: Decoder): LocalDate {
317
return LocalDate.parse(decoder.decodeString())
318
}
319
}
320
```
321
322
### Format Implementation Guidance
323
324
Descriptors help format implementations understand data structure:
325
326
```kotlin
327
fun encodeStructure(descriptor: SerialDescriptor, encoder: Encoder) {
328
when (descriptor.kind) {
329
StructureKind.CLASS -> encodeObject(descriptor, encoder)
330
StructureKind.LIST -> encodeArray(descriptor, encoder)
331
StructureKind.MAP -> encodeMap(descriptor, encoder)
332
is PrimitiveKind -> encodePrimitive(descriptor, encoder)
333
else -> throw SerializationException("Unsupported kind: ${descriptor.kind}")
334
}
335
}
336
```
337
338
## Error Handling
339
340
Descriptor-related errors typically indicate structural mismatches:
341
342
- **IndexOutOfBoundsException**: Invalid element index access
343
- **IllegalArgumentException**: Invalid descriptor configurations
344
- **SerializationException**: Descriptor/data structure mismatches during encoding/decoding