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_—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_—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 453data class Project( 454 val name: String, 455 @EncodeDefault val language: String = "Kotlin" 456) 457``` 458 459This annotation instructs the framework to always serialize property, regardless of its value or format settings. 460It's also possible to tweak it into the opposite behavior using [EncodeDefault.Mode] parameter: 461 462```kotlin 463 464@Serializable 465data class User( 466 val name: String, 467 @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList() 468) 469 470fun main() { 471 val userA = User("Alice", listOf(Project("kotlinx.serialization"))) 472 val userB = User("Bob") 473 println(Json.encodeToString(userA)) 474 println(Json.encodeToString(userB)) 475} 476``` 477 478> You can get the full code [here](../guide/example/example-classes-10.kt). 479 480As you can see, `language` property is preserved and `projects` is omitted: 481 482```text 483{"name":"Alice","projects":[{"name":"kotlinx.serialization","language":"Kotlin"}]} 484{"name":"Bob"} 485``` 486 487<!--- TEST --> 488 489### Nullable properties 490 491Nullable properties are natively supported by Kotlin Serialization. 492 493```kotlin 494@Serializable 495class Project(val name: String, val renamedTo: String? = null) 496 497fun main() { 498 val data = Project("kotlinx.serialization") 499 println(Json.encodeToString(data)) 500} 501``` 502 503> You can get the full code [here](../guide/example/example-classes-11.kt). 504 505This example does not encode `null` in JSON because [Defaults are not encoded](#defaults-are-not-encoded). 506 507```text 508{"name":"kotlinx.serialization"} 509``` 510 511<!--- TEST --> 512 513### Type safety is enforced 514 515Kotlin Serialization strongly enforces the type safety of the Kotlin programming language. 516In particular, let us try to decode a `null` value from a JSON object into a non-nullable Kotlin property `language`. 517 518```kotlin 519@Serializable 520data class Project(val name: String, val language: String = "Kotlin") 521 522fun main() { 523 val data = Json.decodeFromString<Project>(""" 524 {"name":"kotlinx.serialization","language":null} 525 """) 526 println(data) 527} 528``` 529 530> You can get the full code [here](../guide/example/example-classes-12.kt). 531 532Even though the `language` property has a default value, it is still an error to attempt to assign 533the `null` value to it. 534 535```text 536Exception 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 537Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value. 538``` 539 540<!--- TEST LINES_START --> 541 542> It might be desired, when decoding 3rd-party JSONs, to coerce `null` to a default value. 543> The corresponding feature is explained in the [Coercing input values](json.md#coercing-input-values) section. 544 545### Referenced objects 546 547Serializable classes can reference other classes in their serializable properties. 548The referenced classes must be also marked as `@Serializable`. 549 550```kotlin 551@Serializable 552class Project(val name: String, val owner: User) 553 554@Serializable 555class User(val name: String) 556 557fun main() { 558 val owner = User("kotlin") 559 val data = Project("kotlinx.serialization", owner) 560 println(Json.encodeToString(data)) 561} 562``` 563 564> You can get the full code [here](../guide/example/example-classes-13.kt). 565 566When encoded to JSON it results in a nested JSON object. 567 568```text 569{"name":"kotlinx.serialization","owner":{"name":"kotlin"}} 570``` 571 572> References to non-serializable classes can be marked as [Transient properties](#transient-properties), or a 573> custom serializer can be provided for them as shown in the [Serializers](serializers.md) chapter. 574 575<!--- TEST --> 576 577### No compression of repeated references 578 579Kotlin Serialization is designed for encoding and decoding of plain data. It does not support reconstruction 580of arbitrary object graphs with repeated object references. For example, let us try to serialize an object 581that references the same `owner` instance twice. 582 583```kotlin 584@Serializable 585class Project(val name: String, val owner: User, val maintainer: User) 586 587@Serializable 588class User(val name: String) 589 590fun main() { 591 val owner = User("kotlin") 592 val data = Project("kotlinx.serialization", owner, owner) 593 println(Json.encodeToString(data)) 594} 595``` 596 597> You can get the full code [here](../guide/example/example-classes-14.kt). 598 599We simply get the `owner` value encoded twice. 600 601```text 602{"name":"kotlinx.serialization","owner":{"name":"kotlin"},"maintainer":{"name":"kotlin"}} 603``` 604 605> Attempt to serialize a circular structure will result in stack overflow. 606> You can use the [Transient properties](#transient-properties) to exclude some references from serialization. 607 608<!--- TEST --> 609 610### Generic classes 611 612Generic classes in Kotlin provide type-polymorphic behavior, which is enforced by Kotlin Serialization at 613compile-time. For example, consider a generic serializable class `Box<T>`. 614 615```kotlin 616@Serializable 617class Box<T>(val contents: T) 618``` 619 620The `Box<T>` class can be used with builtin types like `Int`, as well as with user-defined types like `Project`. 621 622<!--- INCLUDE 623 624@Serializable 625data class Project(val name: String, val language: String) 626--> 627 628```kotlin 629@Serializable 630class Data( 631 val a: Box<Int>, 632 val b: Box<Project> 633) 634 635fun main() { 636 val data = Data(Box(42), Box(Project("kotlinx.serialization", "Kotlin"))) 637 println(Json.encodeToString(data)) 638} 639``` 640 641> You can get the full code [here](../guide/example/example-classes-15.kt). 642 643The actual type that we get in JSON depends on the actual compile-time type parameter that was specified for `Box`. 644 645```text 646{"a":{"contents":42},"b":{"contents":{"name":"kotlinx.serialization","language":"Kotlin"}}} 647``` 648 649<!--- TEST --> 650 651If the actual generic type is not serializable a compile-time error will be produced. 652 653### Serial field names 654 655The names of the properties used in encoded representation, JSON in our examples, are the same as 656their names in the source code by default. The name that is used for serialization is called a _serial name_, and 657can be changed using the [`@SerialName`][SerialName] annotation. For example, we can have a `language` property in 658the source with an abbreviated serial name. 659 660```kotlin 661@Serializable 662class Project(val name: String, @SerialName("lang") val language: String) 663 664fun main() { 665 val data = Project("kotlinx.serialization", "Kotlin") 666 println(Json.encodeToString(data)) 667} 668``` 669 670> You can get the full code [here](../guide/example/example-classes-16.kt). 671 672Now we see that an abbreviated name `lang` is used in the JSON output. 673 674```text 675{"name":"kotlinx.serialization","lang":"Kotlin"} 676``` 677 678<!--- TEST --> 679 680--- 681 682The next chapter covers [Builtin classes](builtin-classes.md). 683 684<!-- stdlib references --> 685[kotlin.jvm.Transient]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-transient/ 686 687<!--- MODULE /kotlinx-serialization-core --> 688<!--- INDEX kotlinx-serialization-core/kotlinx.serialization --> 689 690[kotlinx.serialization.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/encode-to-string.html 691[Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html 692[kotlinx.serialization.decodeFromString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/decode-from-string.html 693[Required]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-required/index.html 694[Transient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-transient/index.html 695[EncodeDefault]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/index.html 696[EncodeDefault.Mode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/-mode/index.html 697[SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html 698 699<!--- MODULE /kotlinx-serialization-json --> 700<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json --> 701<!--- END --> 702