0
# Encoding and Decoding
1
2
The encoding and decoding system provides the core interfaces that serialization formats use to read and write structured data. These interfaces abstract away format-specific details, allowing serializers to work with any format that implements the encoder/decoder contracts.
3
4
## Capabilities
5
6
### Core Encoding Interfaces
7
8
The fundamental interfaces for writing serialized data in a format-agnostic way.
9
10
```kotlin { .api }
11
/**
12
* Core serialization primitive that encapsulates format-specific knowledge for writing data.
13
* Provides methods for encoding primitive values and initiating structured encoding.
14
*/
15
interface Encoder {
16
/** The serializers module for resolving contextual and polymorphic serializers */
17
val serializersModule: SerializersModule
18
19
/** Begin encoding a structured value, returning a CompositeEncoder for the structure */
20
fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder
21
22
/** Encode a boolean value */
23
fun encodeBoolean(value: Boolean)
24
25
/** Encode a byte value */
26
fun encodeByte(value: Byte)
27
28
/** Encode a char value */
29
fun encodeChar(value: Char)
30
31
/** Encode a double value */
32
fun encodeDouble(value: Double)
33
34
/** Encode an enum value */
35
fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)
36
37
/** Encode a float value */
38
fun encodeFloat(value: Float)
39
40
/** Encode an inline value (value class) */
41
fun encodeInline(descriptor: SerialDescriptor): Encoder
42
43
/** Encode an int value */
44
fun encodeInt(value: Int)
45
46
/** Encode a long value */
47
fun encodeLong(value: Long)
48
49
/** Encode a null value */
50
fun encodeNull()
51
52
/** Encode a short value */
53
fun encodeShort(value: Short)
54
55
/** Encode a string value */
56
fun encodeString(value: String)
57
}
58
59
/**
60
* Part of the encoding process that handles structured data like classes, lists, and maps.
61
* Bound to a specific structured part of the serialized form.
62
*/
63
interface CompositeEncoder {
64
/** The serializers module for resolving contextual and polymorphic serializers */
65
val serializersModule: SerializersModule
66
67
/** Finish encoding the structure */
68
fun endStructure(descriptor: SerialDescriptor)
69
70
/** Check if element at given index should be encoded */
71
fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean
72
73
/** Encode a boolean element at the specified index */
74
fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean)
75
76
/** Encode a byte element at the specified index */
77
fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte)
78
79
/** Encode a char element at the specified index */
80
fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char)
81
82
/** Encode a double element at the specified index */
83
fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double)
84
85
/** Encode a float element at the specified index */
86
fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float)
87
88
/** Encode an inline element at the specified index */
89
fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder
90
91
/** Encode an int element at the specified index */
92
fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int)
93
94
/** Encode a long element at the specified index */
95
fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long)
96
97
/** Encode a null element at the specified index */
98
fun encodeNullableSerializableElement(
99
descriptor: SerialDescriptor,
100
index: Int,
101
serializer: SerializationStrategy<T>,
102
value: T?
103
)
104
105
/** Encode a serializable element at the specified index */
106
fun encodeSerializableElement(
107
descriptor: SerialDescriptor,
108
index: Int,
109
serializer: SerializationStrategy<T>,
110
value: T
111
)
112
113
/** Encode a short element at the specified index */
114
fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short)
115
116
/** Encode a string element at the specified index */
117
fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String)
118
}
119
```
120
121
### Core Decoding Interfaces
122
123
The fundamental interfaces for reading serialized data in a format-agnostic way.
124
125
```kotlin { .api }
126
/**
127
* Core deserialization primitive for format-agnostic decoding of structured data.
128
* Provides methods for decoding primitive values and initiating structured decoding.
129
*/
130
interface Decoder {
131
/** The serializers module for resolving contextual and polymorphic serializers */
132
val serializersModule: SerializersModule
133
134
/** Begin decoding a structured value, returning a CompositeDecoder for the structure */
135
fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder
136
137
/** Decode a boolean value */
138
fun decodeBoolean(): Boolean
139
140
/** Decode a byte value */
141
fun decodeByte(): Byte
142
143
/** Decode a char value */
144
fun decodeChar(): Char
145
146
/** Decode a double value */
147
fun decodeDouble(): Double
148
149
/** Decode an enum value, returning the index */
150
fun decodeEnum(enumDescriptor: SerialDescriptor): Int
151
152
/** Decode a float value */
153
fun decodeFloat(): Float
154
155
/** Decode an inline value (value class) */
156
fun decodeInline(descriptor: SerialDescriptor): Decoder
157
158
/** Decode an int value */
159
fun decodeInt(): Int
160
161
/** Decode a long value */
162
fun decodeLong(): Long
163
164
/** Decode a null value, returning false if null */
165
@ExperimentalSerializationApi
166
fun decodeNotNullMark(): Boolean
167
168
/** Decode a null value */
169
@ExperimentalSerializationApi
170
fun decodeNull(): Nothing?
171
172
/** Decode a short value */
173
fun decodeShort(): Short
174
175
/** Decode a string value */
176
fun decodeString(): String
177
}
178
179
/**
180
* Part of the decoding process that handles structured data like classes, lists, and maps.
181
* Bound to a specific structured part of the serialized form.
182
*/
183
interface CompositeDecoder {
184
/** The serializers module for resolving contextual and polymorphic serializers */
185
val serializersModule: SerializersModule
186
187
/** Finish decoding the structure */
188
fun endStructure(descriptor: SerialDescriptor)
189
190
/** Decode the next element index, returning CompositeDecoder.DECODE_DONE when finished */
191
fun decodeElementIndex(descriptor: SerialDescriptor): Int
192
193
/** Decode a boolean element at the specified index */
194
fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean
195
196
/** Decode a byte element at the specified index */
197
fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte
198
199
/** Decode a char element at the specified index */
200
fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char
201
202
/** Decode a double element at the specified index */
203
fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double
204
205
/** Decode a float element at the specified index */
206
fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float
207
208
/** Decode an inline element at the specified index */
209
fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder
210
211
/** Decode an int element at the specified index */
212
fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int
213
214
/** Decode a long element at the specified index */
215
fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long
216
217
/** Decode a nullable serializable element at the specified index */
218
fun decodeNullableSerializableElement(
219
descriptor: SerialDescriptor,
220
index: Int,
221
deserializer: DeserializationStrategy<T?>,
222
previousValue: T? = null
223
): T?
224
225
/** Decode a serializable element at the specified index */
226
fun decodeSerializableElement(
227
descriptor: SerialDescriptor,
228
index: Int,
229
deserializer: DeserializationStrategy<T>,
230
previousValue: T? = null
231
): T
232
233
/** Decode a short element at the specified index */
234
fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short
235
236
/** Decode a string element at the specified index */
237
fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String
238
239
companion object {
240
/** Constant indicating that decoding is complete */
241
const val DECODE_DONE: Int = -1
242
243
/** Constant indicating unknown element index */
244
const val UNKNOWN_NAME: Int = -3
245
}
246
}
247
```
248
249
### Abstract Base Classes
250
251
Skeleton implementations that provide common functionality for format implementations.
252
253
```kotlin { .api }
254
/**
255
* Skeleton implementation of both Encoder and CompositeEncoder.
256
* Provides default implementations for common encoding patterns.
257
*/
258
abstract class AbstractEncoder : Encoder, CompositeEncoder {
259
/** Default implementation delegates to CompositeEncoder methods */
260
final override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this
261
262
/** Default implementation calls endStructure */
263
override fun endStructure(descriptor: SerialDescriptor) {}
264
265
/** Default implementation returns true for required elements, false for optional */
266
override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean =
267
!descriptor.isElementOptional(index)
268
}
269
270
/**
271
* Skeleton implementation of both Decoder and CompositeDecoder.
272
* Provides default implementations for common decoding patterns.
273
*/
274
abstract class AbstractDecoder : Decoder, CompositeDecoder {
275
/** Default implementation delegates to CompositeDecoder methods */
276
final override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = this
277
278
/** Default implementation calls endStructure */
279
override fun endStructure(descriptor: SerialDescriptor) {}
280
}
281
```
282
283
### Specialized Interfaces
284
285
Additional interfaces for specific encoding/decoding scenarios.
286
287
```kotlin { .api }
288
/**
289
* Decoder interface that supports consuming large strings by chunks.
290
* Useful for streaming formats that need to process large text data efficiently.
291
*/
292
interface ChunkedDecoder {
293
/**
294
* Decodes a string value by chunks, calling the consumer for each chunk.
295
* @param consumeChunk Function to consume each string chunk
296
*/
297
fun decodeStringChunked(consumeChunk: (chunk: String) -> Unit)
298
}
299
```
300
301
### Encoding Extension Functions
302
303
Convenient extension functions that simplify common encoding patterns.
304
305
```kotlin { .api }
306
/**
307
* Encodes structured values using CompositeEncoder in a convenient DSL style.
308
* @param descriptor The descriptor for the structure being encoded
309
* @param block Lambda that performs the encoding using CompositeEncoder
310
*/
311
inline fun Encoder.encodeStructure(
312
descriptor: SerialDescriptor,
313
crossinline block: CompositeEncoder.() -> Unit
314
)
315
316
/**
317
* Convenience method for encoding collections with consistent element handling.
318
* @param descriptor The descriptor for the collection
319
* @param collection The collection to encode
320
* @param block Lambda to encode each collection element
321
*/
322
inline fun <T> Encoder.encodeCollection(
323
descriptor: SerialDescriptor,
324
collection: Collection<T>,
325
crossinline block: CompositeEncoder.(index: Int, element: T) -> Unit
326
)
327
```
328
329
### Decoding Extension Functions
330
331
Convenient extension functions that simplify common decoding patterns.
332
333
```kotlin { .api }
334
/**
335
* Decodes structured values using CompositeDecoder in a convenient DSL style.
336
* @param descriptor The descriptor for the structure being decoded
337
* @param block Lambda that performs the decoding using CompositeDecoder
338
* @return The decoded value
339
*/
340
inline fun <T> Decoder.decodeStructure(
341
descriptor: SerialDescriptor,
342
crossinline block: CompositeDecoder.() -> T
343
): T
344
```
345
346
**Usage Examples:**
347
348
```kotlin
349
import kotlinx.serialization.*
350
import kotlinx.serialization.descriptors.*
351
import kotlinx.serialization.encoding.*
352
353
// Custom serializer using encoding interfaces
354
object PointSerializer : KSerializer<Point> {
355
override val descriptor = buildClassSerialDescriptor("Point") {
356
element("x", PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT))
357
element("y", PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT))
358
}
359
360
override fun serialize(encoder: Encoder, value: Point) {
361
encoder.encodeStructure(descriptor) {
362
encodeIntElement(descriptor, 0, value.x)
363
encodeIntElement(descriptor, 1, value.y)
364
}
365
}
366
367
override fun deserialize(decoder: Decoder): Point {
368
return decoder.decodeStructure(descriptor) {
369
var x = 0
370
var y = 0
371
372
while (true) {
373
when (val index = decodeElementIndex(descriptor)) {
374
0 -> x = decodeIntElement(descriptor, index)
375
1 -> y = decodeIntElement(descriptor, index)
376
CompositeDecoder.DECODE_DONE -> break
377
else -> error("Unexpected index: $index")
378
}
379
}
380
381
Point(x, y)
382
}
383
}
384
}
385
```
386
387
## Implementation Patterns
388
389
### Format Implementation
390
391
When implementing a new format, you typically extend the abstract base classes:
392
393
```kotlin
394
import kotlinx.serialization.encoding.*
395
import kotlinx.serialization.modules.*
396
397
class MyFormatEncoder : AbstractEncoder() {
398
override val serializersModule: SerializersModule = EmptySerializersModule
399
400
override fun encodeString(value: String) {
401
// Format-specific string encoding
402
}
403
404
override fun encodeInt(value: Int) {
405
// Format-specific int encoding
406
}
407
408
// Implement other encoding methods...
409
}
410
411
class MyFormatDecoder : AbstractDecoder() {
412
override val serializersModule: SerializersModule = EmptySerializersModule
413
414
override fun decodeString(): String {
415
// Format-specific string decoding
416
}
417
418
override fun decodeInt(): Int {
419
// Format-specific int decoding
420
}
421
422
// Implement other decoding methods...
423
}
424
```
425
426
### Sequential vs Random Access
427
428
Different formats may require different decoding approaches:
429
430
```kotlin
431
// Sequential decoding (streaming formats)
432
override fun deserialize(decoder: Decoder): MyClass {
433
return decoder.decodeStructure(descriptor) {
434
var field1: String? = null
435
var field2: Int? = null
436
437
while (true) {
438
when (val index = decodeElementIndex(descriptor)) {
439
0 -> field1 = decodeStringElement(descriptor, index)
440
1 -> field2 = decodeIntElement(descriptor, index)
441
CompositeDecoder.DECODE_DONE -> break
442
else -> error("Unexpected index: $index")
443
}
444
}
445
446
MyClass(field1!!, field2!!)
447
}
448
}
449
450
// Random access decoding (structured formats like JSON objects)
451
override fun deserialize(decoder: Decoder): MyClass {
452
return decoder.decodeStructure(descriptor) {
453
MyClass(
454
decodeStringElement(descriptor, 0),
455
decodeIntElement(descriptor, 1)
456
)
457
}
458
}
459
```
460
461
## Error Handling
462
463
Common encoding/decoding errors and their causes:
464
465
- **SerializationException**: General encoding/decoding failures
466
- **IllegalStateException**: Invalid encoder/decoder state transitions
467
- **IndexOutOfBoundsException**: Invalid element index access
468
- **ClassCastException**: Type mismatches during decoding