• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--- TEST_NAME BasicSerializationTest -->
2
3# Basic Serialization
4
5This is the first chapter of the [Kotlin Serialization Guide](serialization-guide.md).
6This chapter shows the basic use of Kotlin Serialization and explains its core concepts.
7
8**Table of contents**
9
10<!--- TOC -->
11
12* [Basics](#basics)
13  * [JSON encoding](#json-encoding)
14  * [JSON decoding](#json-decoding)
15* [Serializable classes](#serializable-classes)
16  * [Backing fields are serialized](#backing-fields-are-serialized)
17  * [Constructor properties requirement](#constructor-properties-requirement)
18  * [Data validation](#data-validation)
19  * [Optional properties](#optional-properties)
20  * [Optional property initializer call](#optional-property-initializer-call)
21  * [Required properties](#required-properties)
22  * [Transient properties](#transient-properties)
23  * [Defaults are not encoded by default](#defaults-are-not-encoded-by-default)
24  * [Nullable properties](#nullable-properties)
25  * [Type safety is enforced](#type-safety-is-enforced)
26  * [Referenced objects](#referenced-objects)
27  * [No compression of repeated references](#no-compression-of-repeated-references)
28  * [Generic classes](#generic-classes)
29  * [Serial field names](#serial-field-names)
30
31<!--- END -->
32
33## Basics
34
35To convert an object tree to a string or to a sequence of bytes, it must come
36through two mutually intertwined processes. In the first step, an object is _serialized_&mdash;it
37is converted into a serial sequence of its constituting primitive values. This process is common for all
38data formats and its result depends on the object being serialized. A _serializer_ controls this process.
39The second step is called _encoding_&mdash;it is the conversion of the corresponding sequence of primitives into
40the output format representation. An _encoder_ controls this process. Whenever the distinction is not important,
41both the terms of encoding and serialization are used interchangeably.
42
43```
44+---------+  Serialization  +------------+  Encoding  +---------------+
45| Objects | --------------> | Primitives | ---------> | Output format |
46+---------+                 +------------+            +---------------+
47```
48
49The reverse process starts with parsing of the input format and _decoding_ of primitive values,
50followed by _deserialization_ of the resulting stream into objects. We'll see details of this process later.
51
52For now, we start with [JSON](https://json.org) encoding.
53
54<!--- INCLUDE .*-basic-.*
55import kotlinx.serialization.*
56import kotlinx.serialization.json.*
57-->
58
59### JSON encoding
60
61The whole process of converting data into a specific format is called _encoding_. For JSON we encode data
62using the [Json.encodeToString][kotlinx.serialization.encodeToString] extension function. It serializes
63the object that is passed as its parameter under the hood and encodes it to a JSON string.
64
65Let's start with a class describing a project and try to get its JSON representation.
66
67```kotlin
68class Project(val name: String, val language: String)
69
70fun main() {
71    val data = Project("kotlinx.serialization", "Kotlin")
72    println(Json.encodeToString(data))
73}
74```
75
76> You can get the full code [here](../guide/example/example-basic-01.kt).
77
78When we run this code we get the exception.
79
80```text
81Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Project' is not found.
82Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
83```
84
85<!--- TEST LINES_START -->
86
87Serializable classes have to be explicitly marked. Kotlin Serialization does not use reflection,
88so you cannot accidentally deserialize a class which was not supposed to be serializable. We fix it by
89adding the [`@Serializable`][Serializable] annotation.
90
91```kotlin
92@Serializable
93class Project(val name: String, val language: String)
94
95fun main() {
96    val data = Project("kotlinx.serialization", "Kotlin")
97    println(Json.encodeToString(data))
98}
99```
100
101> You can get the full code [here](../guide/example/example-basic-02.kt).
102
103The `@Serializable` annotation instructs the Kotlin Serialization plugin to automatically generate and hook
104up a _serializer_ for this class. Now the output of the example is the corresponding JSON.
105
106```text
107{"name":"kotlinx.serialization","language":"Kotlin"}
108```
109
110<!--- TEST -->
111
112> There is a whole chapter about the [Serializers](serializers.md). For now, it is enough to know
113> that they are automatically generated by the Kotlin Serialization plugin.
114
115### JSON decoding
116
117The reverse process is called _decoding_. To decode a JSON string into an object, we'll
118use the [Json.decodeFromString][kotlinx.serialization.decodeFromString] extension function.
119To specify which type we want to get as a result, we provide a type parameter to this function.
120
121As we'll see later, serialization works with different kinds of classes.
122Here we are marking our `Project` class as a `data class`, not because it is required, but because
123we want to print its contents to verify how it decodes.
124
125```kotlin
126@Serializable
127data class Project(val name: String, val language: String)
128
129fun main() {
130    val data = Json.decodeFromString<Project>("""
131        {"name":"kotlinx.serialization","language":"Kotlin"}
132    """)
133    println(data)
134}
135```
136
137> You can get the full code [here](../guide/example/example-basic-03.kt).
138
139Running this code we get back the object.
140
141```text
142Project(name=kotlinx.serialization, language=Kotlin)
143```
144
145<!--- TEST -->
146
147## Serializable classes
148
149This section goes into more details on how different `@Serializable` classes are handled.
150
151<!--- INCLUDE .*-classes-.*
152import kotlinx.serialization.*
153import kotlinx.serialization.json.*
154-->
155
156### Backing fields are serialized
157
158Only a class's properties with backing fields are serialized, so properties with a getter/setter that don't
159have a backing field and delegated properties are not serialized, as the following example shows.
160
161```kotlin
162@Serializable
163class Project(
164    // name is a property with backing field -- serialized
165    var name: String
166) {
167    var stars: Int = 0 // property with a backing field -- serialized
168
169    val path: String // getter only, no backing field -- not serialized
170        get() = "kotlin/$name"
171
172    var id by ::name // delegated property -- not serialized
173}
174
175fun main() {
176    val data = Project("kotlinx.serialization").apply { stars = 9000 }
177    println(Json.encodeToString(data))
178}
179```
180
181> You can get the full code [here](../guide/example/example-classes-01.kt).
182
183We can clearly see that only the `name` and `stars` properties are present in the JSON output.
184
185```text
186{"name":"kotlinx.serialization","stars":9000}
187```
188
189<!--- TEST -->
190
191### Constructor properties requirement
192
193If we want to define the `Project` class so that it takes a path string, and then
194deconstructs it into the corresponding properties, we might be tempted to write something like the code below.
195
196```kotlin
197@Serializable
198class Project(path: String) {
199    val owner: String = path.substringBefore('/')
200    val name: String = path.substringAfter('/')
201}
202```
203
204<!--- CLEAR -->
205
206This class does not compile because the `@Serializable` annotation requires that all parameters of the class's primary
207constructor be properties. A simple workaround is to define a private primary constructor with the class's
208properties, and turn the constructor we wanted into the secondary one.
209
210```kotlin
211@Serializable
212class Project private constructor(val owner: String, val name: String) {
213    constructor(path: String) : this(
214        owner = path.substringBefore('/'),
215        name = path.substringAfter('/')
216    )
217
218    val path: String
219        get() = "$owner/$name"
220}
221```
222
223Serialization works with a private primary constructor, and still serializes only backing fields.
224
225```kotlin
226fun main() {
227    println(Json.encodeToString(Project("kotlin/kotlinx.serialization")))
228}
229```
230
231> You can get the full code [here](../guide/example/example-classes-02.kt).
232
233This example produces the expected output.
234
235```text
236{"owner":"kotlin","name":"kotlinx.serialization"}
237```
238
239<!--- TEST -->
240
241### Data validation
242
243Another case where you might want to introduce a primary constructor parameter without a property is when you
244want to validate its value before storing it to a property. To make it serializable you shall replace it
245with a property in the primary constructor, and move the validation to an `init { ... }` block.
246
247```kotlin
248@Serializable
249class Project(val name: String) {
250    init {
251        require(name.isNotEmpty()) { "name cannot be empty" }
252    }
253}
254```
255
256A deserialization process works like a regular constructor in Kotlin and calls all `init` blocks, ensuring that you
257cannot get an invalid class as a result of deserialization. Let's try it.
258
259```kotlin
260fun main() {
261    val data = Json.decodeFromString<Project>("""
262        {"name":""}
263    """)
264    println(data)
265}
266```
267
268> You can get the full code [here](../guide/example/example-classes-03.kt).
269
270Running this code produces the exception:
271
272```text
273Exception in thread "main" java.lang.IllegalArgumentException: name cannot be empty
274```
275
276<!--- TEST LINES_START -->
277
278### Optional properties
279
280An object can be deserialized only when all its properties are present in the input.
281For example, run the following code.
282
283```kotlin
284@Serializable
285data class Project(val name: String, val language: String)
286
287fun main() {
288    val data = Json.decodeFromString<Project>("""
289        {"name":"kotlinx.serialization"}
290    """)
291    println(data)
292}
293```
294
295> You can get the full code [here](../guide/example/example-classes-04.kt).
296
297It produces the exception:
298
299```text
300Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses04.Project', but it was missing at path: $
301```
302
303<!--- TEST LINES_START -->
304
305This problem can be fixed by adding a default value to the property, which automatically makes it optional
306for serialization.
307
308```kotlin
309@Serializable
310data class Project(val name: String, val language: String = "Kotlin")
311
312fun main() {
313    val data = Json.decodeFromString<Project>("""
314        {"name":"kotlinx.serialization"}
315    """)
316    println(data)
317}
318```
319
320> You can get the full code [here](../guide/example/example-classes-05.kt).
321
322It produces the following output with the default value for the `language` property.
323
324```text
325Project(name=kotlinx.serialization, language=Kotlin)
326```
327
328<!--- TEST -->
329
330### Optional property initializer call
331
332When an optional property is present in the input, the corresponding initializer for this
333property is not even called. This is a feature designed for performance, so be careful not
334to rely on side effects in initializers. Consider the example below.
335
336```kotlin
337fun computeLanguage(): String {
338    println("Computing")
339    return "Kotlin"
340}
341
342@Serializable
343data class Project(val name: String, val language: String = computeLanguage())
344
345fun main() {
346    val data = Json.decodeFromString<Project>("""
347        {"name":"kotlinx.serialization","language":"Kotlin"}
348    """)
349    println(data)
350}
351```
352
353> You can get the full code [here](../guide/example/example-classes-06.kt).
354
355Since the `language` property was specified in the input, we don't see the "Computing" string printed
356in the output.
357
358```text
359Project(name=kotlinx.serialization, language=Kotlin)
360```
361
362<!--- TEST -->
363
364### Required properties
365
366A property with a default value can be required in a serial format with the [`@Required`][Required] annotation.
367Let us change the previous example by marking the `language` property as `@Required`.
368
369```kotlin
370@Serializable
371data class Project(val name: String, @Required val language: String = "Kotlin")
372
373fun main() {
374    val data = Json.decodeFromString<Project>("""
375        {"name":"kotlinx.serialization"}
376    """)
377    println(data)
378}
379```
380
381> You can get the full code [here](../guide/example/example-classes-07.kt).
382
383We get the following exception.
384
385```text
386Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses07.Project', but it was missing at path: $
387```
388
389<!--- TEST LINES_START -->
390
391### Transient properties
392
393A property can be excluded from serialization by marking it with the [`@Transient`][Transient] annotation
394(don't confuse it with [kotlin.jvm.Transient]). Transient properties must have a default value.
395
396```kotlin
397@Serializable
398data class Project(val name: String, @Transient val language: String = "Kotlin")
399
400fun main() {
401    val data = Json.decodeFromString<Project>("""
402        {"name":"kotlinx.serialization","language":"Kotlin"}
403    """)
404    println(data)
405}
406```
407
408> You can get the full code [here](../guide/example/example-classes-08.kt).
409
410Attempts to explicitly specify its value in the serial format, even if the specified
411value is equal to the default one, produces the following exception.
412
413```text
414Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'language' at path: $.name
415Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys.
416```
417
418<!--- TEST LINES_START -->
419
420> The 'ignoreUnknownKeys' feature is explained in the [Ignoring Unknown Keys section](json.md#ignoring-unknown-keys) section.
421
422### Defaults are not encoded by default
423
424Default values are not encoded by default in JSON. This behavior is motivated by the fact that in most real-life scenarios
425such configuration reduces visual clutter, and saves the amount of data being serialized.
426
427```kotlin
428@Serializable
429data class Project(val name: String, val language: String = "Kotlin")
430
431fun main() {
432    val data = Project("kotlinx.serialization")
433    println(Json.encodeToString(data))
434}
435```
436
437> You can get the full code [here](../guide/example/example-classes-09.kt).
438
439It produces the following output, which does not have the `language` property because its value is equal to the default one.
440
441```text
442{"name":"kotlinx.serialization"}
443```
444
445<!--- TEST -->
446
447See JSON's [Encoding defaults](json.md#encoding-defaults) section on how this behavior can be configured for JSON.
448Additionally, this behavior can be controlled without taking format settings into account.
449For that purposes, [EncodeDefault] annotation can be used:
450
451```kotlin
452@Serializable
453@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
454data class Project(
455    val name: String,
456    @EncodeDefault val language: String = "Kotlin"
457)
458```
459
460This annotation instructs the framework to always serialize property, regardless of its value or format settings.
461It's also possible to tweak it into the opposite behavior using [EncodeDefault.Mode] parameter:
462
463```kotlin
464
465@Serializable
466@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
467data class User(
468    val name: String,
469    @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList()
470)
471
472fun main() {
473    val userA = User("Alice", listOf(Project("kotlinx.serialization")))
474    val userB = User("Bob")
475    println(Json.encodeToString(userA))
476    println(Json.encodeToString(userB))
477}
478```
479
480> You can get the full code [here](../guide/example/example-classes-10.kt).
481
482As you can see, `language` property is preserved and `projects` is omitted:
483
484```text
485{"name":"Alice","projects":[{"name":"kotlinx.serialization","language":"Kotlin"}]}
486{"name":"Bob"}
487```
488
489<!--- TEST -->
490
491### Nullable properties
492
493Nullable properties are natively supported by Kotlin Serialization.
494
495```kotlin
496@Serializable
497class Project(val name: String, val renamedTo: String? = null)
498
499fun main() {
500    val data = Project("kotlinx.serialization")
501    println(Json.encodeToString(data))
502}
503```
504
505> You can get the full code [here](../guide/example/example-classes-11.kt).
506
507This example does not encode `null` in JSON because [Defaults are not encoded](#defaults-are-not-encoded).
508
509```text
510{"name":"kotlinx.serialization"}
511```
512
513<!--- TEST -->
514
515### Type safety is enforced
516
517Kotlin Serialization strongly enforces the type safety of the Kotlin programming language.
518In particular, let us try to decode a `null` value from a JSON object into a non-nullable Kotlin property `language`.
519
520```kotlin
521@Serializable
522data class Project(val name: String, val language: String = "Kotlin")
523
524fun main() {
525    val data = Json.decodeFromString<Project>("""
526        {"name":"kotlinx.serialization","language":null}
527    """)
528    println(data)
529}
530```
531
532> You can get the full code [here](../guide/example/example-classes-12.kt).
533
534Even though the `language` property has a default value, it is still an error to attempt to assign
535the `null` value to it.
536
537```text
538Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 52: Expected string literal but 'null' literal was found at path: $.language
539Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value.
540```
541
542<!--- TEST LINES_START -->
543
544> It might be desired, when decoding 3rd-party JSONs, to coerce `null` to a default value.
545> The corresponding feature is explained in the [Coercing input values](json.md#coercing-input-values) section.
546
547### Referenced objects
548
549Serializable classes can reference other classes in their serializable properties.
550The referenced classes must be also marked as `@Serializable`.
551
552```kotlin
553@Serializable
554class Project(val name: String, val owner: User)
555
556@Serializable
557class User(val name: String)
558
559fun main() {
560    val owner = User("kotlin")
561    val data = Project("kotlinx.serialization", owner)
562    println(Json.encodeToString(data))
563}
564```
565
566> You can get the full code [here](../guide/example/example-classes-13.kt).
567
568When encoded to JSON it results in a nested JSON object.
569
570```text
571{"name":"kotlinx.serialization","owner":{"name":"kotlin"}}
572```
573
574> References to non-serializable classes can be marked as [Transient properties](#transient-properties), or a
575> custom serializer can be provided for them as shown in the [Serializers](serializers.md) chapter.
576
577<!--- TEST -->
578
579### No compression of repeated references
580
581Kotlin Serialization is designed for encoding and decoding of plain data. It does not support reconstruction
582of arbitrary object graphs with repeated object references. For example, let us try to serialize an object
583that references the same `owner` instance twice.
584
585```kotlin
586@Serializable
587class Project(val name: String, val owner: User, val maintainer: User)
588
589@Serializable
590class User(val name: String)
591
592fun main() {
593    val owner = User("kotlin")
594    val data = Project("kotlinx.serialization", owner, owner)
595    println(Json.encodeToString(data))
596}
597```
598
599> You can get the full code [here](../guide/example/example-classes-14.kt).
600
601We simply get the `owner` value encoded twice.
602
603```text
604{"name":"kotlinx.serialization","owner":{"name":"kotlin"},"maintainer":{"name":"kotlin"}}
605```
606
607> Attempt to serialize a circular structure will result in stack overflow.
608> You can use the [Transient properties](#transient-properties) to exclude some references from serialization.
609
610<!--- TEST -->
611
612### Generic classes
613
614Generic classes in Kotlin provide type-polymorphic behavior, which is enforced by Kotlin Serialization at
615compile-time. For example, consider a generic serializable class `Box<T>`.
616
617```kotlin
618@Serializable
619class Box<T>(val contents: T)
620```
621
622The `Box<T>` class can be used with builtin types like `Int`, as well as with user-defined types like `Project`.
623
624<!--- INCLUDE
625
626@Serializable
627data class Project(val name: String, val language: String)
628-->
629
630```kotlin
631@Serializable
632class Data(
633    val a: Box<Int>,
634    val b: Box<Project>
635)
636
637fun main() {
638    val data = Data(Box(42), Box(Project("kotlinx.serialization", "Kotlin")))
639    println(Json.encodeToString(data))
640}
641```
642
643> You can get the full code [here](../guide/example/example-classes-15.kt).
644
645The actual type that we get in JSON depends on the actual compile-time type parameter that was specified for `Box`.
646
647```text
648{"a":{"contents":42},"b":{"contents":{"name":"kotlinx.serialization","language":"Kotlin"}}}
649```
650
651<!--- TEST -->
652
653If the actual generic type is not serializable a compile-time error will be produced.
654
655### Serial field names
656
657The names of the properties used in encoded representation, JSON in our examples, are the same as
658their names in the source code by default. The name that is used for serialization is called a _serial name_, and
659can be changed using the [`@SerialName`][SerialName] annotation. For example, we can have a `language` property in
660the source with an abbreviated serial name.
661
662```kotlin
663@Serializable
664class Project(val name: String, @SerialName("lang") val language: String)
665
666fun main() {
667    val data = Project("kotlinx.serialization", "Kotlin")
668    println(Json.encodeToString(data))
669}
670```
671
672> You can get the full code [here](../guide/example/example-classes-16.kt).
673
674Now we see that an abbreviated name `lang` is used in the JSON output.
675
676```text
677{"name":"kotlinx.serialization","lang":"Kotlin"}
678```
679
680<!--- TEST -->
681
682---
683
684The next chapter covers [Builtin classes](builtin-classes.md).
685
686<!-- stdlib references -->
687[kotlin.jvm.Transient]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-transient/
688
689<!--- MODULE /kotlinx-serialization-core -->
690<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
691
692[kotlinx.serialization.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/encode-to-string.html
693[Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
694[kotlinx.serialization.decodeFromString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/decode-from-string.html
695[Required]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-required/index.html
696[Transient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-transient/index.html
697[EncodeDefault]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/index.html
698[EncodeDefault.Mode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/-mode/index.html
699[SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
700
701<!--- MODULE /kotlinx-serialization-json -->
702<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
703<!--- END -->
704