0
# Serial Descriptors
1
2
Complete reference for SerialDescriptor and related APIs that describe the structure of serializable types in kotlinx.serialization-core-js.
3
4
## Core Interface
5
6
### SerialDescriptor
7
8
Primary interface for describing the structure of serializable types.
9
10
```kotlin
11
interface SerialDescriptor {
12
val serialName: String
13
val kind: SerialKind
14
val elementsCount: Int
15
16
fun getElementName(index: Int): String
17
fun getElementIndex(name: String): Int
18
fun isElementOptional(index: Int): Boolean
19
fun getElementDescriptor(index: Int): SerialDescriptor
20
fun getElementAnnotations(index: Int): List<Annotation>
21
fun isInline: Boolean
22
23
val annotations: List<Annotation>
24
val isNullable: Boolean
25
}
26
```
27
{ .api }
28
29
**Usage:**
30
31
```javascript
32
// Get descriptor from serializer
33
const userSerializer = User.serializer();
34
const descriptor = userSerializer.descriptor;
35
36
// Inspect structure
37
console.log(descriptor.serialName); // "User"
38
console.log(descriptor.kind); // StructureKind.CLASS
39
console.log(descriptor.elementsCount); // 3
40
41
// Iterate elements
42
for (let i = 0; i < descriptor.elementsCount; i++) {
43
const name = descriptor.getElementName(i);
44
const elementDesc = descriptor.getElementDescriptor(i);
45
const optional = descriptor.isElementOptional(i);
46
console.log(`${name}: ${elementDesc.serialName} (optional: ${optional})`);
47
}
48
```
49
50
## Serial Kinds
51
52
### SerialKind
53
54
Base sealed class for categorizing different types of serializable structures.
55
56
```kotlin
57
sealed class SerialKind {
58
final override fun toString(): String
59
}
60
```
61
{ .api }
62
63
### PrimitiveKind
64
65
Represents primitive types that cannot be decomposed further.
66
67
```kotlin
68
sealed class PrimitiveKind : SerialKind() {
69
object BOOLEAN : PrimitiveKind()
70
object BYTE : PrimitiveKind()
71
object CHAR : PrimitiveKind()
72
object SHORT : PrimitiveKind()
73
object INT : PrimitiveKind()
74
object LONG : PrimitiveKind()
75
object FLOAT : PrimitiveKind()
76
object DOUBLE : PrimitiveKind()
77
object STRING : PrimitiveKind()
78
}
79
```
80
{ .api }
81
82
**Usage:**
83
84
```javascript
85
// Check for primitive kinds
86
const stringDesc = String.serializer().descriptor;
87
console.log(stringDesc.kind === PrimitiveKind.STRING); // true
88
89
// Pattern matching on kind
90
function describePrimitive(descriptor) {
91
switch (descriptor.kind) {
92
case PrimitiveKind.BOOLEAN:
93
return "Boolean primitive";
94
case PrimitiveKind.STRING:
95
return "String primitive";
96
case PrimitiveKind.INT:
97
return "Integer primitive";
98
// ... other cases
99
default:
100
return "Not a primitive";
101
}
102
}
103
```
104
105
### StructureKind
106
107
Represents composite types with internal structure.
108
109
```kotlin
110
sealed class StructureKind : SerialKind() {
111
object CLASS : StructureKind()
112
object LIST : StructureKind()
113
object MAP : StructureKind()
114
object OBJECT : StructureKind()
115
}
116
```
117
{ .api }
118
119
**Usage:**
120
121
```javascript
122
// Check structure types
123
const userDesc = User.serializer().descriptor;
124
console.log(userDesc.kind === StructureKind.CLASS); // true
125
126
const listDesc = ListSerializer(String.serializer()).descriptor;
127
console.log(listDesc.kind === StructureKind.LIST); // true
128
129
const mapDesc = MapSerializer(String.serializer(), Int.serializer()).descriptor;
130
console.log(mapDesc.kind === StructureKind.MAP); // true
131
```
132
133
### PolymorphicKind
134
135
Represents types that support polymorphic serialization (Experimental).
136
137
```kotlin
138
@ExperimentalSerializationApi
139
sealed class PolymorphicKind : SerialKind() {
140
object SEALED : PolymorphicKind()
141
object OPEN : PolymorphicKind()
142
}
143
```
144
{ .api }
145
146
**Usage:**
147
148
```javascript
149
// Sealed class descriptor
150
const resultDesc = Result.serializer().descriptor;
151
console.log(resultDesc.kind === PolymorphicKind.SEALED); // true
152
153
// Open polymorphic descriptor
154
const shapeDesc = PolymorphicSerializer(Shape::class).descriptor;
155
console.log(shapeDesc.kind === PolymorphicKind.OPEN); // true
156
```
157
158
## Descriptor Factory Functions
159
160
### buildClassSerialDescriptor
161
162
Factory function for creating class descriptors with a builder DSL.
163
164
```kotlin
165
fun buildClassSerialDescriptor(
166
serialName: String,
167
typeParameters: Array<SerialDescriptor> = emptyArray(),
168
builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
169
): SerialDescriptor
170
```
171
{ .api }
172
173
**Usage:**
174
175
```javascript
176
const userDescriptor = buildClassSerialDescriptor("User") {
177
element("name", String.serializer().descriptor)
178
element("age", Int.serializer().descriptor, isOptional = true)
179
element("email", String.serializer().descriptor)
180
};
181
182
// With type parameters (for generic classes)
183
const pairDescriptor = buildClassSerialDescriptor("Pair", [
184
String.serializer().descriptor, // First type parameter
185
Int.serializer().descriptor // Second type parameter
186
]) {
187
element("first", getTypeParameter(0)) // Use first type parameter
188
element("second", getTypeParameter(1)) // Use second type parameter
189
};
190
```
191
192
### ClassSerialDescriptorBuilder
193
194
Builder class for constructing class descriptors.
195
196
```kotlin
197
class ClassSerialDescriptorBuilder(val serialName: String) {
198
fun element(
199
elementName: String,
200
descriptor: SerialDescriptor,
201
annotations: List<Annotation> = emptyList(),
202
isOptional: Boolean = false
203
)
204
205
fun getTypeParameter(index: Int): SerialDescriptor
206
}
207
```
208
{ .api }
209
210
**Usage:**
211
212
```javascript
213
// Advanced builder usage
214
const advancedDescriptor = buildClassSerialDescriptor("AdvancedClass") {
215
// Required element
216
element("id", String.serializer().descriptor)
217
218
// Optional element
219
element("description", String.serializer().descriptor, isOptional = true)
220
221
// Element with annotations
222
element("timestamp", Long.serializer().descriptor, [
223
SerialName("created_at")
224
])
225
226
// Nested structure
227
element("address", buildClassSerialDescriptor("Address") {
228
element("street", String.serializer().descriptor)
229
element("city", String.serializer().descriptor)
230
})
231
};
232
```
233
234
### PrimitiveSerialDescriptor
235
236
Factory for primitive descriptors.
237
238
```kotlin
239
fun PrimitiveSerialDescriptor(
240
serialName: String,
241
kind: PrimitiveKind
242
): SerialDescriptor
243
```
244
{ .api }
245
246
**Usage:**
247
248
```javascript
249
// Create custom primitive descriptor
250
const customStringDescriptor = PrimitiveSerialDescriptor(
251
"CustomString",
252
PrimitiveKind.STRING
253
);
254
255
// Use in serializer
256
class CustomStringSerializer {
257
constructor() {
258
this.descriptor = customStringDescriptor;
259
}
260
261
serialize(encoder, value) {
262
encoder.encodeString(value.toString().toUpperCase());
263
}
264
265
deserialize(decoder) {
266
return decoder.decodeString().toLowerCase();
267
}
268
}
269
```
270
271
### SerialDescriptor Factory (Wrapper)
272
273
Creates a descriptor that wraps another descriptor.
274
275
```kotlin
276
fun SerialDescriptor(
277
serialName: String,
278
original: SerialDescriptor
279
): SerialDescriptor
280
```
281
{ .api }
282
283
**Usage:**
284
285
```javascript
286
// Create wrapper descriptor with different name
287
const originalDesc = String.serializer().descriptor;
288
const wrappedDesc = SerialDescriptor("WrappedString", originalDesc);
289
290
console.log(wrappedDesc.serialName); // "WrappedString"
291
console.log(wrappedDesc.kind); // PrimitiveKind.STRING (from original)
292
```
293
294
### Collection Descriptors
295
296
Specialized factory functions for collection descriptors (Experimental).
297
298
```kotlin
299
@ExperimentalSerializationApi
300
fun listSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor
301
302
@ExperimentalSerializationApi
303
inline fun <reified T> listSerialDescriptor(): SerialDescriptor
304
305
@ExperimentalSerializationApi
306
fun mapSerialDescriptor(
307
keyDescriptor: SerialDescriptor,
308
valueDescriptor: SerialDescriptor
309
): SerialDescriptor
310
311
@ExperimentalSerializationApi
312
inline fun <reified K, reified V> mapSerialDescriptor(): SerialDescriptor
313
314
@ExperimentalSerializationApi
315
fun setSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor
316
317
@ExperimentalSerializationApi
318
inline fun <reified T> setSerialDescriptor(): SerialDescriptor
319
```
320
{ .api }
321
322
**Usage:**
323
324
```javascript
325
// Collection descriptors
326
const stringListDesc = listSerialDescriptor(String.serializer().descriptor);
327
const reifiedListDesc = listSerialDescriptor<String>();
328
329
const stringIntMapDesc = mapSerialDescriptor(
330
String.serializer().descriptor,
331
Int.serializer().descriptor
332
);
333
const reifiedMapDesc = mapSerialDescriptor<String, Int>();
334
335
const stringSetDesc = setSerialDescriptor(String.serializer().descriptor);
336
const reifiedSetDesc = setSerialDescriptor<String>();
337
```
338
339
## Descriptor Utilities
340
341
### serialDescriptor Functions
342
343
Retrieve descriptors for types.
344
345
```kotlin
346
inline fun <reified T> serialDescriptor(): SerialDescriptor
347
348
fun serialDescriptor(type: KType): SerialDescriptor
349
```
350
{ .api }
351
352
**Usage:**
353
354
```javascript
355
// Get descriptor for reified type
356
const userDesc = serialDescriptor<User>();
357
const listDesc = serialDescriptor<List<String>>();
358
359
// Get descriptor for KType
360
const nullableStringType = typeOf<String?>();
361
const nullableStringDesc = serialDescriptor(nullableStringType);
362
```
363
364
### Nullable Descriptors
365
366
```kotlin
367
val SerialDescriptor.nullable: SerialDescriptor
368
369
@ExperimentalSerializationApi
370
val SerialDescriptor.nonNullOriginal: SerialDescriptor
371
```
372
{ .api }
373
374
**Usage:**
375
376
```javascript
377
const stringDesc = String.serializer().descriptor;
378
const nullableStringDesc = stringDesc.nullable;
379
380
console.log(stringDesc.isNullable); // false
381
console.log(nullableStringDesc.isNullable); // true
382
383
// Get original non-null descriptor
384
const originalDesc = nullableStringDesc.nonNullOriginal;
385
console.log(originalDesc === stringDesc); // true
386
```
387
388
### Iteration Utilities
389
390
```kotlin
391
val SerialDescriptor.elementDescriptors: Iterable<SerialDescriptor>
392
val SerialDescriptor.elementNames: Iterable<String>
393
```
394
{ .api }
395
396
**Usage:**
397
398
```javascript
399
const userDesc = User.serializer().descriptor;
400
401
// Iterate element names
402
for (const name of userDesc.elementNames) {
403
console.log(`Element: ${name}`);
404
}
405
406
// Iterate element descriptors
407
for (const [index, elementDesc] of userDesc.elementDescriptors.entries()) {
408
console.log(`Element ${index}: ${elementDesc.serialName}`);
409
}
410
411
// Combined iteration
412
const names = Array.from(userDesc.elementNames);
413
const descriptors = Array.from(userDesc.elementDescriptors);
414
415
names.forEach((name, index) => {
416
const desc = descriptors[index];
417
console.log(`${name}: ${desc.kind}`);
418
});
419
```
420
421
## Context-Aware Descriptors
422
423
### Captured KClass
424
425
Access to the original KClass for contextual and polymorphic types (Experimental).
426
427
```kotlin
428
@ExperimentalSerializationApi
429
val SerialDescriptor.capturedKClass: KClass<*>?
430
```
431
{ .api }
432
433
**Usage:**
434
435
```javascript
436
const polymorphicDesc = PolymorphicSerializer(Shape::class).descriptor;
437
const capturedClass = polymorphicDesc.capturedKClass;
438
console.log(capturedClass === Shape::class); // true
439
440
const contextualDesc = ContextualSerializer(Date::class).descriptor;
441
const dateClass = contextualDesc.capturedKClass;
442
console.log(dateClass === Date::class); // true
443
```
444
445
### Module Context Functions
446
447
Retrieve contextual information from SerializersModule (Experimental).
448
449
```kotlin
450
@ExperimentalSerializationApi
451
fun SerializersModule.getContextualDescriptor(
452
descriptor: SerialDescriptor
453
): SerialDescriptor?
454
455
@ExperimentalSerializationApi
456
fun SerializersModule.getPolymorphicDescriptors(
457
baseDescriptor: SerialDescriptor
458
): Collection<SerialDescriptor>
459
```
460
{ .api }
461
462
**Usage:**
463
464
```javascript
465
const module = SerializersModule {
466
contextual(Date::class, CustomDateSerializer)
467
polymorphic(Shape::class) {
468
subclass(Circle::class)
469
subclass(Rectangle::class)
470
}
471
};
472
473
// Get contextual descriptor
474
const dateDesc = Date::class.serializer().descriptor;
475
const contextualDesc = module.getContextualDescriptor(dateDesc);
476
477
// Get polymorphic descriptors
478
const shapeDesc = Shape::class.serializer().descriptor;
479
const subDescriptors = module.getPolymorphicDescriptors(shapeDesc);
480
console.log(subDescriptors.length); // 2 (Circle, Rectangle)
481
```
482
483
## Descriptor Inspection
484
485
### Complete Example
486
487
```javascript
488
function inspectDescriptor(descriptor, indent = 0) {
489
const spaces = ' '.repeat(indent);
490
491
console.log(`${spaces}Name: ${descriptor.serialName}`);
492
console.log(`${spaces}Kind: ${descriptor.kind}`);
493
console.log(`${spaces}Nullable: ${descriptor.isNullable}`);
494
console.log(`${spaces}Inline: ${descriptor.isInline}`);
495
console.log(`${spaces}Elements: ${descriptor.elementsCount}`);
496
497
if (descriptor.annotations.length > 0) {
498
console.log(`${spaces}Annotations: ${descriptor.annotations.map(a => a.constructor.name)}`);
499
}
500
501
// Inspect elements for structured types
502
if (descriptor.elementsCount > 0) {
503
for (let i = 0; i < descriptor.elementsCount; i++) {
504
const name = descriptor.getElementName(i);
505
const optional = descriptor.isElementOptional(i);
506
const elementDesc = descriptor.getElementDescriptor(i);
507
const annotations = descriptor.getElementAnnotations(i);
508
509
console.log(`${spaces} [${i}] ${name}${optional ? '?' : ''}:`);
510
if (annotations.length > 0) {
511
console.log(`${spaces} Annotations: ${annotations.map(a => a.constructor.name)}`);
512
}
513
514
// Recursive inspection for nested structures
515
if (elementDesc.elementsCount > 0) {
516
inspectDescriptor(elementDesc, indent + 4);
517
} else {
518
console.log(`${spaces} Type: ${elementDesc.serialName} (${elementDesc.kind})`);
519
}
520
}
521
}
522
}
523
524
// Usage
525
const userDescriptor = User.serializer().descriptor;
526
inspectDescriptor(userDescriptor);
527
```
528
529
### TypeScript Type Guards
530
531
```typescript
532
// Type guards for descriptor kinds
533
function isPrimitive(descriptor: SerialDescriptor): boolean {
534
return descriptor.kind instanceof PrimitiveKind;
535
}
536
537
function isStructured(descriptor: SerialDescriptor): boolean {
538
return descriptor.kind instanceof StructureKind;
539
}
540
541
function isPolymorphic(descriptor: SerialDescriptor): boolean {
542
return descriptor.kind instanceof PolymorphicKind;
543
}
544
545
// Specific structure checks
546
function isClass(descriptor: SerialDescriptor): boolean {
547
return descriptor.kind === StructureKind.CLASS;
548
}
549
550
function isList(descriptor: SerialDescriptor): boolean {
551
return descriptor.kind === StructureKind.LIST;
552
}
553
554
function isMap(descriptor: SerialDescriptor): boolean {
555
return descriptor.kind === StructureKind.MAP;
556
}
557
```
558
559
Serial descriptors provide complete introspection capabilities for serializable types, enabling format implementors and advanced users to understand and work with the structure of any serializable type at runtime.