# Basic Serialization This is the first chapter of the [Kotlin Serialization Guide](serialization-guide.md). This chapter shows the basic use of Kotlin Serialization and explains its core concepts. **Table of contents** * [Basics](#basics) * [JSON encoding](#json-encoding) * [JSON decoding](#json-decoding) * [Serializable classes](#serializable-classes) * [Backing fields are serialized](#backing-fields-are-serialized) * [Constructor properties requirement](#constructor-properties-requirement) * [Data validation](#data-validation) * [Optional properties](#optional-properties) * [Optional property initializer call](#optional-property-initializer-call) * [Required properties](#required-properties) * [Transient properties](#transient-properties) * [Defaults are not encoded by default](#defaults-are-not-encoded-by-default) * [Nullable properties](#nullable-properties) * [Type safety is enforced](#type-safety-is-enforced) * [Referenced objects](#referenced-objects) * [No compression of repeated references](#no-compression-of-repeated-references) * [Generic classes](#generic-classes) * [Serial field names](#serial-field-names) ## Basics To convert an object tree to a string or to a sequence of bytes, it must come through two mutually intertwined processes. In the first step, an object is _serialized_—it is converted into a serial sequence of its constituting primitive values. This process is common for all data formats and its result depends on the object being serialized. A _serializer_ controls this process. The second step is called _encoding_—it is the conversion of the corresponding sequence of primitives into the output format representation. An _encoder_ controls this process. Whenever the distinction is not important, both the terms of encoding and serialization are used interchangeably. ``` +---------+ Serialization +------------+ Encoding +---------------+ | Objects | --------------> | Primitives | ---------> | Output format | +---------+ +------------+ +---------------+ ``` The reverse process starts with parsing of the input format and _decoding_ of primitive values, followed by _deserialization_ of the resulting stream into objects. We'll see details of this process later. For now, we start with [JSON](https://json.org) encoding. ### JSON encoding The whole process of converting data into a specific format is called _encoding_. For JSON we encode data using the [Json.encodeToString][kotlinx.serialization.encodeToString] extension function. It serializes the object that is passed as its parameter under the hood and encodes it to a JSON string. Let's start with a class describing a project and try to get its JSON representation. ```kotlin class Project(val name: String, val language: String) fun main() { val data = Project("kotlinx.serialization", "Kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-basic-01.kt). When we run this code we get the exception. ```text Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Project' is not found. Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied. ``` Serializable classes have to be explicitly marked. Kotlin Serialization does not use reflection, so you cannot accidentally deserialize a class which was not supposed to be serializable. We fix it by adding the [`@Serializable`][Serializable] annotation. ```kotlin @Serializable class Project(val name: String, val language: String) fun main() { val data = Project("kotlinx.serialization", "Kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-basic-02.kt). The `@Serializable` annotation instructs the Kotlin Serialization plugin to automatically generate and hook up a _serializer_ for this class. Now the output of the example is the corresponding JSON. ```text {"name":"kotlinx.serialization","language":"Kotlin"} ``` > There is a whole chapter about the [Serializers](serializers.md). For now, it is enough to know > that they are automatically generated by the Kotlin Serialization plugin. ### JSON decoding The reverse process is called _decoding_. To decode a JSON string into an object, we'll use the [Json.decodeFromString][kotlinx.serialization.decodeFromString] extension function. To specify which type we want to get as a result, we provide a type parameter to this function. As we'll see later, serialization works with different kinds of classes. Here we are marking our `Project` class as a `data class`, not because it is required, but because we want to print its contents to verify how it decodes. ```kotlin @Serializable data class Project(val name: String, val language: String) fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-basic-03.kt). Running this code we get back the object. ```text Project(name=kotlinx.serialization, language=Kotlin) ``` ## Serializable classes This section goes into more details on how different `@Serializable` classes are handled. ### Backing fields are serialized Only a class's properties with backing fields are serialized, so properties with a getter/setter that don't have a backing field and delegated properties are not serialized, as the following example shows. ```kotlin @Serializable class Project( // name is a property with backing field -- serialized var name: String ) { var stars: Int = 0 // property with a backing field -- serialized val path: String // getter only, no backing field -- not serialized get() = "kotlin/$name" var id by ::name // delegated property -- not serialized } fun main() { val data = Project("kotlinx.serialization").apply { stars = 9000 } println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-01.kt). We can clearly see that only the `name` and `stars` properties are present in the JSON output. ```text {"name":"kotlinx.serialization","stars":9000} ``` ### Constructor properties requirement If we want to define the `Project` class so that it takes a path string, and then deconstructs it into the corresponding properties, we might be tempted to write something like the code below. ```kotlin @Serializable class Project(path: String) { val owner: String = path.substringBefore('/') val name: String = path.substringAfter('/') } ``` This class does not compile because the `@Serializable` annotation requires that all parameters of the class's primary constructor be properties. A simple workaround is to define a private primary constructor with the class's properties, and turn the constructor we wanted into the secondary one. ```kotlin @Serializable class Project private constructor(val owner: String, val name: String) { constructor(path: String) : this( owner = path.substringBefore('/'), name = path.substringAfter('/') ) val path: String get() = "$owner/$name" } ``` Serialization works with a private primary constructor, and still serializes only backing fields. ```kotlin fun main() { println(Json.encodeToString(Project("kotlin/kotlinx.serialization"))) } ``` > You can get the full code [here](../guide/example/example-classes-02.kt). This example produces the expected output. ```text {"owner":"kotlin","name":"kotlinx.serialization"} ``` ### Data validation Another case where you might want to introduce a primary constructor parameter without a property is when you want to validate its value before storing it to a property. To make it serializable you shall replace it with a property in the primary constructor, and move the validation to an `init { ... }` block. ```kotlin @Serializable class Project(val name: String) { init { require(name.isNotEmpty()) { "name cannot be empty" } } } ``` A deserialization process works like a regular constructor in Kotlin and calls all `init` blocks, ensuring that you cannot get an invalid class as a result of deserialization. Let's try it. ```kotlin fun main() { val data = Json.decodeFromString(""" {"name":""} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-03.kt). Running this code produces the exception: ```text Exception in thread "main" java.lang.IllegalArgumentException: name cannot be empty ``` ### Optional properties An object can be deserialized only when all its properties are present in the input. For example, run the following code. ```kotlin @Serializable data class Project(val name: String, val language: String) fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-04.kt). It produces the exception: ```text Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses04.Project', but it was missing at path: $ ``` This problem can be fixed by adding a default value to the property, which automatically makes it optional for serialization. ```kotlin @Serializable data class Project(val name: String, val language: String = "Kotlin") fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-05.kt). It produces the following output with the default value for the `language` property. ```text Project(name=kotlinx.serialization, language=Kotlin) ``` ### Optional property initializer call When an optional property is present in the input, the corresponding initializer for this property is not even called. This is a feature designed for performance, so be careful not to rely on side effects in initializers. Consider the example below. ```kotlin fun computeLanguage(): String { println("Computing") return "Kotlin" } @Serializable data class Project(val name: String, val language: String = computeLanguage()) fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-06.kt). Since the `language` property was specified in the input, we don't see the "Computing" string printed in the output. ```text Project(name=kotlinx.serialization, language=Kotlin) ``` ### Required properties A property with a default value can be required in a serial format with the [`@Required`][Required] annotation. Let us change the previous example by marking the `language` property as `@Required`. ```kotlin @Serializable data class Project(val name: String, @Required val language: String = "Kotlin") fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-07.kt). We get the following exception. ```text Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses07.Project', but it was missing at path: $ ``` ### Transient properties A property can be excluded from serialization by marking it with the [`@Transient`][Transient] annotation (don't confuse it with [kotlin.jvm.Transient]). Transient properties must have a default value. ```kotlin @Serializable data class Project(val name: String, @Transient val language: String = "Kotlin") fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-08.kt). Attempts to explicitly specify its value in the serial format, even if the specified value is equal to the default one, produces the following exception. ```text Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'language' at path: $.name Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys. ``` > The 'ignoreUnknownKeys' feature is explained in the [Ignoring Unknown Keys section](json.md#ignoring-unknown-keys) section. ### Defaults are not encoded by default Default values are not encoded by default in JSON. This behavior is motivated by the fact that in most real-life scenarios such configuration reduces visual clutter, and saves the amount of data being serialized. ```kotlin @Serializable data class Project(val name: String, val language: String = "Kotlin") fun main() { val data = Project("kotlinx.serialization") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-09.kt). It produces the following output, which does not have the `language` property because its value is equal to the default one. ```text {"name":"kotlinx.serialization"} ``` See JSON's [Encoding defaults](json.md#encoding-defaults) section on how this behavior can be configured for JSON. Additionally, this behavior can be controlled without taking format settings into account. For that purposes, [EncodeDefault] annotation can be used: ```kotlin @Serializable data class Project( val name: String, @EncodeDefault val language: String = "Kotlin" ) ``` This annotation instructs the framework to always serialize property, regardless of its value or format settings. It's also possible to tweak it into the opposite behavior using [EncodeDefault.Mode] parameter: ```kotlin @Serializable data class User( val name: String, @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List = emptyList() ) fun main() { val userA = User("Alice", listOf(Project("kotlinx.serialization"))) val userB = User("Bob") println(Json.encodeToString(userA)) println(Json.encodeToString(userB)) } ``` > You can get the full code [here](../guide/example/example-classes-10.kt). As you can see, `language` property is preserved and `projects` is omitted: ```text {"name":"Alice","projects":[{"name":"kotlinx.serialization","language":"Kotlin"}]} {"name":"Bob"} ``` ### Nullable properties Nullable properties are natively supported by Kotlin Serialization. ```kotlin @Serializable class Project(val name: String, val renamedTo: String? = null) fun main() { val data = Project("kotlinx.serialization") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-11.kt). This example does not encode `null` in JSON because [Defaults are not encoded](#defaults-are-not-encoded). ```text {"name":"kotlinx.serialization"} ``` ### Type safety is enforced Kotlin Serialization strongly enforces the type safety of the Kotlin programming language. In particular, let us try to decode a `null` value from a JSON object into a non-nullable Kotlin property `language`. ```kotlin @Serializable data class Project(val name: String, val language: String = "Kotlin") fun main() { val data = Json.decodeFromString(""" {"name":"kotlinx.serialization","language":null} """) println(data) } ``` > You can get the full code [here](../guide/example/example-classes-12.kt). Even though the `language` property has a default value, it is still an error to attempt to assign the `null` value to it. ```text Exception 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 Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value. ``` > It might be desired, when decoding 3rd-party JSONs, to coerce `null` to a default value. > The corresponding feature is explained in the [Coercing input values](json.md#coercing-input-values) section. ### Referenced objects Serializable classes can reference other classes in their serializable properties. The referenced classes must be also marked as `@Serializable`. ```kotlin @Serializable class Project(val name: String, val owner: User) @Serializable class User(val name: String) fun main() { val owner = User("kotlin") val data = Project("kotlinx.serialization", owner) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-13.kt). When encoded to JSON it results in a nested JSON object. ```text {"name":"kotlinx.serialization","owner":{"name":"kotlin"}} ``` > References to non-serializable classes can be marked as [Transient properties](#transient-properties), or a > custom serializer can be provided for them as shown in the [Serializers](serializers.md) chapter. ### No compression of repeated references Kotlin Serialization is designed for encoding and decoding of plain data. It does not support reconstruction of arbitrary object graphs with repeated object references. For example, let us try to serialize an object that references the same `owner` instance twice. ```kotlin @Serializable class Project(val name: String, val owner: User, val maintainer: User) @Serializable class User(val name: String) fun main() { val owner = User("kotlin") val data = Project("kotlinx.serialization", owner, owner) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-14.kt). We simply get the `owner` value encoded twice. ```text {"name":"kotlinx.serialization","owner":{"name":"kotlin"},"maintainer":{"name":"kotlin"}} ``` > Attempt to serialize a circular structure will result in stack overflow. > You can use the [Transient properties](#transient-properties) to exclude some references from serialization. ### Generic classes Generic classes in Kotlin provide type-polymorphic behavior, which is enforced by Kotlin Serialization at compile-time. For example, consider a generic serializable class `Box`. ```kotlin @Serializable class Box(val contents: T) ``` The `Box` class can be used with builtin types like `Int`, as well as with user-defined types like `Project`. ```kotlin @Serializable class Data( val a: Box, val b: Box ) fun main() { val data = Data(Box(42), Box(Project("kotlinx.serialization", "Kotlin"))) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-15.kt). The actual type that we get in JSON depends on the actual compile-time type parameter that was specified for `Box`. ```text {"a":{"contents":42},"b":{"contents":{"name":"kotlinx.serialization","language":"Kotlin"}}} ``` If the actual generic type is not serializable a compile-time error will be produced. ### Serial field names The names of the properties used in encoded representation, JSON in our examples, are the same as their names in the source code by default. The name that is used for serialization is called a _serial name_, and can be changed using the [`@SerialName`][SerialName] annotation. For example, we can have a `language` property in the source with an abbreviated serial name. ```kotlin @Serializable class Project(val name: String, @SerialName("lang") val language: String) fun main() { val data = Project("kotlinx.serialization", "Kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-classes-16.kt). Now we see that an abbreviated name `lang` is used in the JSON output. ```text {"name":"kotlinx.serialization","lang":"Kotlin"} ``` --- The next chapter covers [Builtin classes](builtin-classes.md). [kotlin.jvm.Transient]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-transient/ [kotlinx.serialization.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/encode-to-string.html [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html [kotlinx.serialization.decodeFromString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/decode-from-string.html [Required]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-required/index.html [Transient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-transient/index.html [EncodeDefault]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/index.html [EncodeDefault.Mode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/-mode/index.html [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html