0
# Serializers Modules
1
2
Complete reference for SerializersModule and contextual serialization APIs that enable runtime serializer configuration in kotlinx.serialization-core-js.
3
4
## Core Module Class
5
6
### SerializersModule
7
8
Sealed class that holds a collection of serializers for runtime lookup.
9
10
```kotlin
11
public sealed class SerializersModule {
12
@ExperimentalSerializationApi
13
public abstract fun <T : Any> getContextual(
14
kClass: KClass<T>,
15
typeArgumentsSerializers: List<KSerializer<*>> = emptyList()
16
): KSerializer<T>?
17
18
@ExperimentalSerializationApi
19
public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>?
20
21
@ExperimentalSerializationApi
22
public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>?
23
24
@ExperimentalSerializationApi
25
public abstract fun dumpTo(collector: SerializersModuleCollector)
26
}
27
```
28
{ .api }
29
30
**Usage:**
31
32
```javascript
33
// Basic module lookup
34
const module = SerializersModule {
35
contextual(Date::class, CustomDateSerializer)
36
};
37
38
// Get contextual serializer
39
const dateSerializer = module.getContextual(Date::class);
40
if (dateSerializer !== null) {
41
const json = format.encodeToString(dateSerializer, new Date());
42
}
43
44
// Polymorphic lookup
45
const shape = new Circle(5);
46
const shapeSerializer = module.getPolymorphic(Shape::class, shape);
47
```
48
49
## Module Factory Functions
50
51
### SerializersModule Builder
52
53
Primary DSL function for creating modules.
54
55
```kotlin
56
fun SerializersModule(builderAction: SerializersModuleBuilder.() -> Unit): SerializersModule
57
```
58
{ .api }
59
60
**Usage:**
61
62
```javascript
63
const module = SerializersModule {
64
// Add contextual serializers
65
contextual(Date::class, CustomDateSerializer)
66
contextual(BigDecimal::class, BigDecimalStringSerializer)
67
68
// Add polymorphic hierarchies
69
polymorphic(Shape::class) {
70
subclass(Circle::class)
71
subclass(Rectangle::class)
72
subclass(Triangle::class)
73
}
74
75
// Include other modules
76
include(otherModule)
77
};
78
```
79
80
### Empty Module
81
82
Factory for creating an empty module.
83
84
```kotlin
85
fun EmptySerializersModule(): SerializersModule
86
```
87
{ .api }
88
89
**Usage:**
90
91
```javascript
92
// Create empty module
93
const emptyModule = EmptySerializersModule();
94
95
// Use as base for building
96
const customModule = SerializersModule {
97
include(emptyModule)
98
contextual(String::class, CustomStringSerializer)
99
};
100
```
101
102
### Single Serializer Module
103
104
Factory functions for modules with a single serializer.
105
106
```kotlin
107
fun <T : Any> serializersModuleOf(
108
kClass: KClass<T>,
109
serializer: KSerializer<T>
110
): SerializersModule
111
112
inline fun <reified T : Any> serializersModuleOf(
113
serializer: KSerializer<T>
114
): SerializersModule
115
```
116
{ .api }
117
118
**Usage:**
119
120
```javascript
121
// Create module with single contextual serializer
122
const dateModule = serializersModuleOf(Date::class, CustomDateSerializer);
123
const reifiedDateModule = serializersModuleOf<Date>(CustomDateSerializer);
124
125
// Use in format configuration
126
const format = Json {
127
serializersModule = dateModule
128
};
129
```
130
131
## Module Builder
132
133
### SerializersModuleBuilder
134
135
Builder class for constructing SerializersModule instances.
136
137
```kotlin
138
class SerializersModuleBuilder {
139
fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>)
140
fun <T : Any> contextual(kClass: KClass<T>, provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>)
141
142
fun <Base : Any> polymorphic(
143
baseClass: KClass<Base>,
144
actualClass: KClass<out Base>,
145
actualSerializer: KSerializer<out Base>
146
)
147
148
fun <Base : Any> polymorphicDefaultSerializer(
149
baseClass: KClass<Base>,
150
defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
151
)
152
153
fun <Base : Any> polymorphicDefaultDeserializer(
154
baseClass: KClass<Base>,
155
defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
156
)
157
158
fun include(module: SerializersModule)
159
}
160
```
161
{ .api }
162
163
## Contextual Serialization
164
165
### Basic Contextual Registration
166
167
```javascript
168
// Register contextual serializers
169
const module = SerializersModule {
170
// Simple contextual registration
171
contextual(Date::class, CustomDateSerializer)
172
contextual(BigInteger::class, BigIntegerStringSerializer)
173
contextual(UUID::class, UUIDStringSerializer)
174
};
175
176
// Use in serializable classes
177
@Serializable
178
class Event {
179
name;
180
181
@Contextual
182
startTime; // Uses Date serializer from module
183
184
@Contextual
185
eventId; // Uses UUID serializer from module
186
187
constructor(name, startTime, eventId) {
188
this.name = name;
189
this.startTime = startTime;
190
this.eventId = eventId;
191
}
192
}
193
```
194
195
### Reified Contextual Registration
196
197
```kotlin
198
inline fun <reified T : Any> SerializersModuleBuilder.contextual(
199
serializer: KSerializer<T>
200
)
201
```
202
{ .api }
203
204
**Usage:**
205
206
```javascript
207
const module = SerializersModule {
208
// Reified contextual registration
209
contextual<Date>(CustomDateSerializer)
210
contextual<BigDecimal>(BigDecimalSerializer)
211
contextual<LocalDateTime>(LocalDateTimeSerializer)
212
};
213
```
214
215
### Generic Contextual Serializers
216
217
```kotlin
218
fun <T : Any> SerializersModuleBuilder.contextual(
219
kClass: KClass<T>,
220
provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
221
)
222
```
223
{ .api }
224
225
**Usage:**
226
227
```javascript
228
// Generic contextual serializer with type arguments
229
const module = SerializersModule {
230
contextual(Optional::class) { typeArgs ->
231
OptionalSerializer(typeArgs[0]) // Use first type argument
232
}
233
234
contextual(Result::class) { typeArgs ->
235
ResultSerializer(typeArgs[0]) // Generic Result<T> serializer
236
}
237
};
238
239
// Usage with generic types
240
@Serializable
241
class ApiResponse<T> {
242
@Contextual
243
data; // Optional<T> - serializer determined at runtime
244
245
status;
246
247
constructor(data, status) {
248
this.data = data;
249
this.status = status;
250
}
251
}
252
```
253
254
## Polymorphic Serialization
255
256
### Basic Polymorphic Registration
257
258
```javascript
259
const module = SerializersModule {
260
polymorphic(Shape::class) {
261
subclass(Circle::class)
262
subclass(Rectangle::class)
263
subclass(Triangle::class)
264
}
265
266
// Alternative syntax
267
polymorphic(Animal::class, Cat::class, Cat.serializer())
268
polymorphic(Animal::class, Dog::class, Dog.serializer())
269
};
270
```
271
272
### Polymorphic Module Builder
273
274
```kotlin
275
class PolymorphicModuleBuilder<Base : Any> {
276
fun <T : Base> subclass(kClass: KClass<T>)
277
fun <T : Base> subclass(kClass: KClass<T>, serializer: KSerializer<T>)
278
279
fun default(defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?)
280
fun defaultDeserializer(defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?)
281
}
282
```
283
{ .api }
284
285
**Usage:**
286
287
```javascript
288
const module = SerializersModule {
289
polymorphic(Shape::class) {
290
// Register subclasses (uses generated serializers)
291
subclass(Circle::class)
292
subclass(Rectangle::class)
293
294
// Register with custom serializer
295
subclass(Triangle::class, TriangleCustomSerializer)
296
297
// Default serializer for unknown types
298
default { value ->
299
when (value) {
300
is UnknownShape -> UnknownShapeSerializer
301
else -> null
302
}
303
}
304
305
// Default deserializer for unknown class names
306
defaultDeserializer { className ->
307
when (className) {
308
"LegacyShape" -> LegacyShapeSerializer
309
else -> null
310
}
311
}
312
}
313
};
314
```
315
316
### Reified Polymorphic Registration
317
318
```kotlin
319
inline fun <reified T : Base> PolymorphicModuleBuilder<Base>.subclass()
320
inline fun <reified T : Base> PolymorphicModuleBuilder<Base>.subclass(serializer: KSerializer<T>)
321
```
322
{ .api }
323
324
**Usage:**
325
326
```javascript
327
const module = SerializersModule {
328
polymorphic(Shape::class) {
329
// Reified subclass registration
330
subclass<Circle>()
331
subclass<Rectangle>()
332
subclass<Triangle>(TriangleCustomSerializer)
333
}
334
};
335
```
336
337
## Advanced Polymorphic Configuration
338
339
### Custom Base Serializer
340
341
```javascript
342
const module = SerializersModule {
343
// Register polymorphic hierarchy with custom base serializer
344
polymorphic(
345
Shape::class,
346
Shape::class, // base class
347
ShapeBaseSerializer // custom base serializer
348
) {
349
subclass(Circle::class)
350
subclass(Rectangle::class)
351
}
352
};
353
```
354
355
### Default Providers
356
357
```javascript
358
const module = SerializersModule {
359
polymorphic(ApiResponse::class) {
360
subclass(SuccessResponse::class)
361
subclass(ErrorResponse::class)
362
363
// Handle serialization of unknown subtypes
364
default { value ->
365
if (value instanceof UnknownResponse) {
366
return UnknownResponseSerializer;
367
}
368
return null; // Will throw if no serializer found
369
}
370
371
// Handle deserialization of unknown type names
372
defaultDeserializer { className ->
373
if (className === "LegacyResponse") {
374
return LegacyResponseSerializer;
375
}
376
if (className === null) {
377
return DefaultResponseSerializer;
378
}
379
return null;
380
}
381
}
382
};
383
```
384
385
## Module Operations
386
387
### Module Combination
388
389
```kotlin
390
operator fun SerializersModule.plus(other: SerializersModule): SerializersModule
391
392
fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule
393
```
394
{ .api }
395
396
**Usage:**
397
398
```javascript
399
// Combine modules (first module takes precedence for conflicts)
400
const baseModule = SerializersModule {
401
contextual(Date::class, DefaultDateSerializer)
402
polymorphic(Shape::class) {
403
subclass(Circle::class)
404
}
405
};
406
407
const extensionModule = SerializersModule {
408
contextual(Date::class, CustomDateSerializer) // This will be ignored
409
contextual(BigDecimal::class, BigDecimalSerializer)
410
polymorphic(Shape::class) {
411
subclass(Rectangle::class)
412
}
413
};
414
415
val combinedModule = baseModule + extensionModule;
416
417
// Overwrite combination (second module takes precedence)
418
const overwrittenModule = baseModule.overwriteWith(extensionModule);
419
// Now uses CustomDateSerializer instead of DefaultDateSerializer
420
```
421
422
### Module Inclusion
423
424
```javascript
425
// Include existing modules in new ones
426
const coreModule = SerializersModule {
427
contextual(Date::class, DateSerializer)
428
contextual(UUID::class, UUIDSerializer)
429
};
430
431
const extendedModule = SerializersModule {
432
include(coreModule) // Include all registrations from coreModule
433
434
contextual(BigDecimal::class, BigDecimalSerializer)
435
polymorphic(Shape::class) {
436
subclass(Circle::class)
437
}
438
};
439
```
440
441
## Module Introspection
442
443
### SerializersModuleCollector
444
445
Interface for visiting module contents (Experimental).
446
447
```kotlin
448
@ExperimentalSerializationApi
449
interface SerializersModuleCollector {
450
fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>)
451
fun <Base : Any, Sub : Base> polymorphic(
452
baseClass: KClass<Base>,
453
actualClass: KClass<Sub>,
454
actualSerializer: KSerializer<Sub>
455
)
456
fun <Base : Any> polymorphicDefaultSerializer(
457
baseClass: KClass<Base>,
458
defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
459
)
460
fun <Base : Any> polymorphicDefaultDeserializer(
461
baseClass: KClass<Base>,
462
defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
463
)
464
}
465
```
466
{ .api }
467
468
**Usage:**
469
470
```javascript
471
// Collect and inspect module contents
472
class ModuleInspector {
473
constructor() {
474
this.contextualSerializers = new Map();
475
this.polymorphicSerializers = new Map();
476
}
477
478
contextual(kClass, serializer) {
479
console.log(`Contextual: ${kClass.simpleName} -> ${serializer.constructor.name}`);
480
this.contextualSerializers.set(kClass, serializer);
481
}
482
483
polymorphic(baseClass, actualClass, actualSerializer) {
484
console.log(`Polymorphic: ${baseClass.simpleName} -> ${actualClass.simpleName}`);
485
486
if (!this.polymorphicSerializers.has(baseClass)) {
487
this.polymorphicSerializers.set(baseClass, new Map());
488
}
489
this.polymorphicSerializers.get(baseClass).set(actualClass, actualSerializer);
490
}
491
492
polymorphicDefaultSerializer(baseClass, provider) {
493
console.log(`Default serializer for ${baseClass.simpleName}`);
494
}
495
496
polymorphicDefaultDeserializer(baseClass, provider) {
497
console.log(`Default deserializer for ${baseClass.simpleName}`);
498
}
499
}
500
501
// Inspect module
502
const inspector = new ModuleInspector();
503
module.dumpTo(inspector);
504
505
console.log("Contextual serializers:", inspector.contextualSerializers);
506
console.log("Polymorphic serializers:", inspector.polymorphicSerializers);
507
```
508
509
## Format Integration
510
511
### Using Modules with Formats
512
513
```javascript
514
// Configure JSON format with module
515
const module = SerializersModule {
516
contextual(Date::class, ISO8601DateSerializer)
517
contextual(BigDecimal::class, BigDecimalStringSerializer)
518
519
polymorphic(ApiResponse::class) {
520
subclass(SuccessResponse::class)
521
subclass(ErrorResponse::class)
522
}
523
};
524
525
const json = Json {
526
serializersModule = module
527
ignoreUnknownKeys = true
528
encodeDefaults = false
529
};
530
531
// All serialization operations use the module
532
@Serializable
533
class ApiCall {
534
@Contextual
535
timestamp; // Uses ISO8601DateSerializer
536
537
@Contextual
538
amount; // Uses BigDecimalStringSerializer
539
540
@Polymorphic
541
response; // Uses polymorphic serialization
542
543
constructor(timestamp, amount, response) {
544
this.timestamp = timestamp;
545
this.amount = amount;
546
this.response = response;
547
}
548
}
549
550
const call = new ApiCall(new Date(), new BigDecimal("123.45"), new SuccessResponse("OK"));
551
const jsonString = json.encodeToString(ApiCall.serializer(), call);
552
```
553
554
## Real-World Examples
555
556
### Complete Application Module
557
558
```javascript
559
// Comprehensive module for a real application
560
const applicationModule = SerializersModule {
561
// Date/time serialization
562
contextual(Date::class, ISO8601DateSerializer)
563
contextual(LocalDateTime::class, LocalDateTimeComponentSerializer)
564
contextual(Duration::class, DurationStringSerializer)
565
566
// Numeric types for precision
567
contextual(BigDecimal::class, BigDecimalStringSerializer)
568
contextual(Long::class, LongAsStringSerializer)
569
570
// Identity types
571
contextual(UUID::class, UUIDStringSerializer)
572
573
// API response hierarchy
574
polymorphic(ApiResponse::class) {
575
subclass(SuccessResponse::class)
576
subclass(ErrorResponse::class)
577
subclass(ValidationErrorResponse::class)
578
579
default { response ->
580
when (response.type) {
581
"unknown" -> UnknownResponseSerializer
582
else -> null
583
}
584
}
585
}
586
587
// Domain model hierarchy
588
polymorphic(DomainEvent::class) {
589
subclass(UserCreatedEvent::class)
590
subclass(OrderPlacedEvent::class)
591
subclass(PaymentProcessedEvent::class)
592
}
593
594
// Generic result type
595
contextual(Result::class) { typeArgs ->
596
ResultSerializer(
597
successSerializer = typeArgs[0],
598
errorSerializer = typeArgs.getOrNull(1) ?: String.serializer()
599
)
600
}
601
};
602
```
603
604
### Module Composition Pattern
605
606
```javascript
607
// Modular approach with separate concerns
608
const dateTimeModule = SerializersModule {
609
contextual(Date::class, ISO8601DateSerializer)
610
contextual(LocalDateTime::class, LocalDateTimeSerializer)
611
contextual(Duration::class, DurationISOSerializer)
612
};
613
614
const numericModule = SerializersModule {
615
contextual(BigDecimal::class, BigDecimalStringSerializer)
616
contextual(Long::class, LongAsStringSerializer)
617
};
618
619
const apiModule = SerializersModule {
620
polymorphic(ApiResponse::class) {
621
subclass(SuccessResponse::class)
622
subclass(ErrorResponse::class)
623
}
624
};
625
626
const domainModule = SerializersModule {
627
polymorphic(DomainEvent::class) {
628
subclass(UserCreatedEvent::class)
629
subclass(OrderPlacedEvent::class)
630
}
631
};
632
633
// Compose final application module
634
const applicationModule = SerializersModule {
635
include(dateTimeModule)
636
include(numericModule)
637
include(apiModule)
638
include(domainModule)
639
};
640
641
// Use in different configurations
642
const jsonConfig = Json {
643
serializersModule = applicationModule
644
ignoreUnknownKeys = true
645
};
646
647
const protobufConfig = ProtoBuf {
648
serializersModule = applicationModule
649
};
650
```
651
652
SerializersModule provides a powerful and flexible system for runtime serializer configuration, enabling complex serialization scenarios while maintaining type safety and performance across different formats.