## 1. Project Overview & Quickstart (Kotlin/kotlinx.serialization) ## File: README.md # Kotlin multiplatform / multi-format reflectionless serialization [](https://kotlinlang.org/docs/components-stability.html) [](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [](http://www.apache.org/licenses/LICENSE-2.0) [](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxSerialization_Ko&guest=1) [](http://kotlinlang.org) [](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.11.0) [](https://kotlinlang.org/api/kotlinx.serialization/) [](https://kotlinlang.slack.com/messages/serialization/) Kotlin serialization consists of a compiler plugin, that generates visitor code for serializable classes, runtime library with core serialization API and support libraries with various serialization formats. * Supports Kotlin classes marked as `@Serializable` and standard collections. * Provides [JSON](formats/README.md#JSON), [Protobuf](formats/README.md#ProtoBuf), [CBOR](formats/README.md#CBOR), [Hocon](formats/README.md#HOCON) and [Properties](formats/README.md#properties) formats. * Complete multiplatform support: JVM, JS and Native. ## Table of contents * [Introduction and references](#introduction-and-references) * [Setup](#setup) * [Gradle](#gradle) * [1) Setting up the serialization plugin](#1-setting-up-the-serialization-plugin) * [2) Dependency on the JSON library](#2-dependency-on-the-json-library) * [Android](#android) * [Multiplatform (Common, JS, Native)](#multiplatform-common-js-native) * [Maven](#maven) * [Bazel](#bazel) * **Additional links** * [Kotlin Serialization Guide](docs/serialization-guide.md) * [Full API reference](https://kotlinlang.org/api/kotlinx.serialization/) * [Submitting issues and PRs](CONTRIBUTING.md) * [Building this library](docs/building.md) ## Introduction and references Here is a small example. ```kotlin import kotlinx.serialization.* import kotlinx.serialization.json.* @Serializable data class Project(val name: String, val language: String) fun main() { // Serializing objects val data = Project("kotlinx.serialization", "Kotlin") val string = Json.encodeToString(data) println(string) // {"name":"kotlinx.serialization","language":"Kotlin"} // Deserializing back into objects val obj = Json.decodeFromString(string) println(obj) // Project(name=kotlinx.serialization, language=Kotlin) } ``` > You can get the full code [here](guide/example/example-readme-01.kt). **Read the [Kotlin Serialization Guide](docs/serialization-guide.md) for all details.** You can find auto-generated documentation website on [kotlinlang.org](https://kotlinlang.org/api/kotlinx.serialization/). ## Setup [New versions](https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.serialization) of the serialization plugin are released in tandem with each new Kotlin compiler version. Make sure you have the corresponding Kotlin plugin installed in the IDE, no additional plugins for IDE are required. ### Gradle To set up kotlinx.serialization, you have to do two things: 1) Add the **[serialization plugin](#1-setting-up-the-serialization-plugin)**. 2) Add the **[serialization library dependency](#2-dependency-on-the-json-library)**. #### 1) Setting up the serialization plugin You can set up the serialization plugin with the Kotlin plugin using the [Gradle plugins DSL](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block): Kotlin DSL: ```kotlin plugins { kotlin("jvm") version "2.3.20" // or kotlin("multiplatform") or any other kotlin plugin kotlin("plugin.serialization") version "2.3.20" } ``` Groovy DSL: ```gradle plugins { id 'org.jetbrains.kotlin.multiplatform' version '2.3.20' id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.20' } ``` > Kotlin versions before 1.4.0 are not supported by the stable release of Kotlin serialization. Using `apply plugin` (the old way) First, you have to add the serialization plugin to your classpath as the other [compiler plugins](https://kotlinlang.org/docs/reference/compiler-plugins.html): Kotlin DSL: ```kotlin buildscript { repositories { mavenCentral() } dependencies { val kotlinVersion = "2.3.20" classpath(kotlin("gradle-plugin", version = kotlinVersion)) classpath(kotlin("serialization", version = kotlinVersion)) } } ``` Groovy DSL: ```gradle buildscript { ext.kotlin_version = '2.3.20' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" } } ``` Then you can `apply plugin` (example in Groovy): ```gradle apply plugin: 'kotlin' // or 'kotlin-multiplatform' for multiplatform projects apply plugin: 'kotlinx-serialization' ``` #### 2) Dependency on the JSON library After setting up the plugin, you have to add a dependency on the serialization library. Note that while the plugin has version the same as the compiler one, runtime library has different coordinates, repository and versioning. Kotlin DSL: ```kotlin repositories { mavenCentral() } dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0") } ``` Groovy DSL: ```gradle repositories { mavenCentral() } dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0" } ``` >We also provide `kotlinx-serialization-core` artifact that contains all serialization API but does not have a bundled serialization format with it ### Android By default, proguard rules are supplied with the library. [These rules](rules/common.pro) keep serializers for _all_ serializable classes that are retained after shrinking, so you don't need additional setup. **However, these rules do not affect serializable classes if they have named companion objects.** If you want to serialize classes with named companion objects, you need to add and edit rules below to your `proguard-rules.pro` configuration. Note that the rules for R8 differ depending on the [compatibility mode](https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md) used. Example of named companion rules for ProGuard and R8 compatibility mode ```proguard # Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`. # If you have any, replace classes with those containing named companion objects. -keepattributes InnerClasses # Needed for `getDeclaredClasses`. -if @kotlinx.serialization.Serializable class com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions. com.example.myapplication.HasNamedCompanion2 { static **$* *; } -keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept. static <1>$$serializer INSTANCE; } ``` Example of named companion rules for R8 full mode ```proguard # Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`. # If you have any, replace classes with those containing named companion objects. -keepattributes InnerClasses # Needed for `getDeclaredClasses`. -if @kotlinx.serialization.Serializable class com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions. com.example.myapplication.HasNamedCompanion2 { static **$* *; } -keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept. static <1>$$serializer INSTANCE; } # Keep both serializer and serializable classes to save the attribute InnerClasses -keepclasseswithmembers, allowshrinking, allowobfuscation, allowaccessmodification class com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions. com.example.myapplication.HasNamedCompanion2 { *; } ``` In case you want to exclude serializable classes that are used, but never serialized at runtime, you will need to write custom rules with narrower [class specifications](https://www.guardsquare.com/manual/configuration/usage). ### Multiplatform (Common, JS, Native) Most of the modules are also available for Kotlin/JS and Kotlin/Native. You can add dependency to the required module right to the common source set: ```gradle commonMain { dependencies { // Works as common dependency as well as the platform one implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version" } } ``` The same artifact coordinates can be used to depend on platform-specific artifact in platform-specific source-set. ### Maven Ensure the proper version of Kotlin and serialization version: ```xml 2.3.20 1.11.0 ``` Add serialization plugin to Kotlin compiler plugin: ```xml org.jetbrains.kotlin kotlin-maven-plugin ${kotlin.version} compile compile compile kotlinx-serialization org.jetbrains.kotlin kotlin-maven-serialization ${kotlin.version} ``` Add dependency on serialization runtime library: ```xml org.jetbrains.kotlinx kotlinx-serialization-json ${serialization.version} ``` ### Bazel To setup the Kotlin compiler plugin for Bazel, follow [the example](https://github.com/bazelbuild/rules_kotlin/tree/master/examples/plugin/src/serialization) from the `rules_kotlin` repository. --- ## File: docs/basic-serialization.md # 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.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'language' at path: $ Use 'ignoreUnknownKeys = true' in 'Json {}' builder or '@JsonIgnoreUnknownKeys' annotation 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(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.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 --- ## File: docs/building.md # Building Kotlin Serialization from the source ## JDK version To build Kotlin Serialization JDK version 11 or higher is required. Make sure this is your default JDK (`JAVA_HOME` is set accordingly). This is needed to compile the `module-info` file included for JPMS support. In case you are determined to use a different JDK version or experience problems with JPMS, you can turn off compilation of module-info files completely with `disableJPMS` property: add `disableJPMS=true` to gradle.properties or `-PdisableJPMS` to Gradle CLI invocation. ## Runtime library Kotlin Serialization runtime library itself is a [multiplatform](http://kotlinlang.org/docs/reference/multiplatform.html) project. To build the library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest,` `jsTest,` `macosArm64Test`, and so on are also available. The project can be opened in IntelliJ IDEA without additional prerequisites. In case you want to work with Protobuf tests, you may need to run `./gradlew generateTestProto` beforehand. To install runtime library into the local Maven repository, run `./gradlew publishToMavenLocal`. After that, you can include this library in arbitrary projects like usual gradle dependency: ```gradle repositories { mavenLocal() } dependencies { compile "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version" } ``` To use snapshot version of compiler (if you have built and installed it from sources), use flag `-Pbootstrap`. If you have built both Kotlin and Kotlin/Native compilers, set `kotlin.native.home` property in `gradle.properties` to the path with Kotlin/Native distribution (usually `kotlin-native/dist` folder inside Kotlin project). The `master` and `dev` branches of the library should be binary compatible with the latest released compiler plugin. In case you want to test some new features from other branches, which are still in development and may not be compatible in terms of bytecode produced by plugin, you'll need to build the plugin by yourself. ## Compiler plugin Compiler plugins for Gradle/Maven and IntelliJ plugin, starting from Kotlin 1.3, are embedded into the Kotlin compiler. Sources and steps to build it are located [here](https://github.com/JetBrains/kotlin/tree/master/plugins/kotlinx-serialization). In short, you'll just need to run `./gradlew dist install` to get `2.x.255-SNAPSHOT` versions of Kotlin compiler, stdlib, and serialization plugins in the Maven local repository. --- ## File: docs/builtin-classes.md # Builtin classes This is the second chapter of the [Kotlin Serialization Guide](serialization-guide.md). In addition to all the primitive types and strings, serialization for some classes from the Kotlin standard library, including the standard collections, is built into Kotlin Serialization. This chapter explains the details. **Table of contents** * [Primitives](#primitives) * [Numbers](#numbers) * [Long numbers](#long-numbers) * [Long numbers as strings](#long-numbers-as-strings) * [Enum classes](#enum-classes) * [Serial names of enum entries](#serial-names-of-enum-entries) * [Composites](#composites) * [Pair and triple](#pair-and-triple) * [Lists](#lists) * [Sets and other collections](#sets-and-other-collections) * [Deserializing collections](#deserializing-collections) * [Maps](#maps) * [Unit and singleton objects](#unit-and-singleton-objects) * [Duration](#duration) * [Nothing](#nothing) ## Primitives Kotlin Serialization has the following ten primitives: `Boolean`, `Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `Char`, `String`, and enums. The other types in Kotlin Serialization are _composite_—composed of those primitive values. ### Numbers All types of integer and floating-point Kotlin numbers can be serialized. ```kotlin @Serializable class Data( val answer: Int, val pi: Double ) fun main() { val data = Data(42, PI) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-builtin-01.kt). Their natural representation in JSON is used. ```text {"answer":42,"pi":3.141592653589793} ``` ### Long numbers Long integers are serializable, too. ```kotlin @Serializable class Data(val signature: Long) fun main() { val data = Data(0x1CAFE2FEED0BABE0) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-builtin-02.kt). By default they are serialized to JSON as numbers. ```text {"signature":2067120338512882656} ``` ### Long numbers as strings The JSON output from the previous example will get decoded normally by Kotlin Serialization running on Kotlin/JS. However, if we try to parse this JSON by native JavaScript methods, we get this truncated result. ``` JSON.parse("{\"signature\":2067120338512882656}") ▶ {signature: 2067120338512882700} ``` The full range of a Kotlin Long does not fit in the JavaScript number, so its precision gets lost in JavaScript. A common workaround is to represent long numbers with full precision using the JSON string type. This approach is optionally supported by Kotlin Serialization with [LongAsStringSerializer], which can be specified for a given Long property using the [`@Serializable`][Serializable] annotation: ```kotlin @Serializable class Data( @Serializable(with=LongAsStringSerializer::class) val signature: Long ) fun main() { val data = Data(0x1CAFE2FEED0BABE0) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-builtin-03.kt). This JSON gets parsed natively by JavaScript without loss of precision. ```text {"signature":"2067120338512882656"} ``` > The section on [Specifying serializers for a file](serializers.md#specifying-serializers-for-a-file) explains how a > serializer like `LongAsStringSerializer` can be specified for all properties in a file. ### Enum classes All enum classes are serializable out of the box without having to mark them `@Serializable`, as the following example shows. ```kotlin // The @Serializable annotation is not needed for enum classes enum class Status { SUPPORTED } @Serializable class Project(val name: String, val status: Status) fun main() { val data = Project("kotlinx.serialization", Status.SUPPORTED) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-builtin-04.kt). In JSON an enum gets encoded as a string. ```text {"name":"kotlinx.serialization","status":"SUPPORTED"} ``` > Note: On Kotlin/JS and Kotlin/Native, `@Serializable` annotation is needed for enum class if you want to use it as a root object — i.e. use `encodeToString(Status.SUPPORTED)`. ### Serial names of enum entries Serial names of enum entries can be customized with the [SerialName] annotation just like it was shown for properties in the [Serial field names](basic-serialization.md#serial-field-names) section. However, in this case, the whole enum class must be marked with the [`@Serializable`][Serializable] annotation. ```kotlin @Serializable // required because of @SerialName enum class Status { @SerialName("maintained") SUPPORTED } @Serializable class Project(val name: String, val status: Status) fun main() { val data = Project("kotlinx.serialization", Status.SUPPORTED) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-builtin-05.kt). We see that the specified serial name is now used in the resulting JSON. ```text {"name":"kotlinx.serialization","status":"maintained"} ``` ## Composites A number of composite types from the standard library are supported by Kotlin Serialization. ### Pair and triple The simple data classes [Pair] and [Triple] from the Kotlin standard library are serializable. ```kotlin @Serializable class Project(val name: String) fun main() { val pair = 1 to Project("kotlinx.serialization") println(Json.encodeToString(pair)) } ``` > You can get the full code [here](../guide/example/example-builtin-06.kt). ```text {"first":1,"second":{"name":"kotlinx.serialization"}} ``` > Not all classes from the Kotlin standard library are serializable. In particular, ranges and the [Regex] class > are not serializable at the moment. Support for their serialization may be added in the future. ### Lists A [List] of serializable classes can be serialized. ```kotlin @Serializable class Project(val name: String) fun main() { val list = listOf( Project("kotlinx.serialization"), Project("kotlinx.coroutines") ) println(Json.encodeToString(list)) } ``` > You can get the full code [here](../guide/example/example-builtin-07.kt). The result is represented as a list in JSON. ```text [{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}] ``` ### Sets and other collections Other collections, like [Set], are also serializable. ```kotlin @Serializable class Project(val name: String) fun main() { val set = setOf( Project("kotlinx.serialization"), Project("kotlinx.coroutines") ) println(Json.encodeToString(set)) } ``` > You can get the full code [here](../guide/example/example-builtin-08.kt). [Set] is also represented as a list in JSON, like all other collections. ```text [{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}] ``` ### Deserializing collections During deserialization, the type of the resulting object is determined by the static type that was specified in the source code—either as the type of the property or as the type parameter of the decoding function. The following example shows how the same JSON list of integers is deserialized into two properties of different Kotlin types. ```kotlin @Serializable data class Data( val a: List, val b: Set ) fun main() { val data = Json.decodeFromString(""" { "a": [42, 42], "b": [42, 42] } """) println(data) } ``` > You can get the full code [here](../guide/example/example-builtin-09.kt). Because the `data.b` property is a [Set], the duplicate values from it disappeared. ```text Data(a=[42, 42], b=[42]) ``` ### Maps A [Map] with primitive or enum keys and arbitrary serializable values can be serialized. ```kotlin @Serializable class Project(val name: String) fun main() { val map = mapOf( 1 to Project("kotlinx.serialization"), 2 to Project("kotlinx.coroutines") ) println(Json.encodeToString(map)) } ``` > You can get the full code [here](../guide/example/example-builtin-10.kt). Kotlin maps in JSON are represented as objects. In JSON object keys are always strings, so keys are encoded as strings even if they are numbers in Kotlin, as we can see below. ```text {"1":{"name":"kotlinx.serialization"},"2":{"name":"kotlinx.coroutines"}} ``` > It is a JSON-specific limitation that keys cannot be composite. > It can be lifted as shown in the [Allowing structured map keys](json.md#allowing-structured-map-keys) section. ### Unit and singleton objects The Kotlin builtin [Unit](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/) type is also serializable. `Unit` is a Kotlin [singleton object](https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview), and is handled equally with other Kotlin objects. Conceptually, a singleton is a class with only one instance, meaning that state does not define the object, but the object defines its state. In JSON, objects are serialized as empty structures. ```kotlin @Serializable object SerializationVersion { val libraryVersion: String = "1.0.0" } fun main() { println(Json.encodeToString(SerializationVersion)) println(Json.encodeToString(Unit)) } ``` > You can get the full code [here](../guide/example/example-builtin-11.kt). While it may seem useless at first glance, this comes in handy for sealed class serialization, which is explained in the [Polymorphism. Objects](polymorphism.md#objects) section. ```text {} {} ``` > Serialization of objects is format specific. Other formats may represent objects differently, > e.g. using their fully qualified names. ### Duration Since Kotlin `1.7.20` the [Duration] class has become serializable. ```kotlin fun main() { val duration = 1000.toDuration(DurationUnit.SECONDS) println(Json.encodeToString(duration)) } ``` > You can get the full code [here](../guide/example/example-builtin-12.kt). Duration is serialized as a string in the ISO-8601-2 format. ```text "PT16M40S" ``` ## Nothing By default, [Nothing] is a serializable class. However, since there are no instances of this class, it is impossible to encode or decode its values - any attempt will cause an exception. This serializer is used when syntactically some type is needed, but it is not actually used in serialization. For example, when using parameterized polymorphic base classes: ```kotlin @Serializable sealed class ParametrizedParent { @Serializable data class ChildWithoutParameter(val value: Int) : ParametrizedParent() } fun main() { println(Json.encodeToString(ParametrizedParent.ChildWithoutParameter(42))) } ``` > You can get the full code [here](../guide/example/example-builtin-13.kt). When encoding, the serializer for `Nothing` was not used ```text {"value":42} ``` --- The next chapter covers [Serializers](serializers.md). [Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html [Pair]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/ [Triple]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/ [Regex]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-regex/ [List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/ [Set]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/ [Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/ [Duration]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/ [Nothing]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-nothing.html [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html [LongAsStringSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-long-as-string-serializer/index.html --- ## File: docs/compatibility.md # Compatibility policy This document describes the compatibility policy of kotlinx.serialization library since version 1.0.0 and Kotlin 1.4.0. Note that content of this document is applicable only for **stable** Kotlin platforms (currently Kotlin/JVM and classic Kotlin/JS), since other experimental platforms currently do not impose any backward-compatibility guarantees. You can check out what platforms are considered to be stable on [this page](https://kotlinlang.org/docs/reference/evolution/components-stability.html). - [Core library compatibility](#core-library-compatibility) * [General (Stable) API](#stable-api) * [Experimental API](#experimental-api) * [Internal API](#internal-api) - [Compatibility with Kotlin compiler plugin](#compatibility-with-kotlin-compiler-plugin) ## Core library compatibility Core library public API comes in three flavours: general (stable), experimental, and internal. All public API except stable is marked with the corresponding annotation. To learn how to use declarations that require opt-in, please refer to [corresponding documentation page](https://kotlinlang.org/docs/reference/opt-in-requirements.html#non-propagating-use). ### Stable API Stable API is guaranteed to preserve its ABI and documented semantics: * It cannot change its semantics expressed in its documentation. * It is binary backwards-compatible: during update of `kotlinx.serialization` version, previously compiled code will continue to work. For example, for a library that depends only on `kotlinx.serialization` stable API, clients of the library can easily depend on a next `kotlinx.serialization` version and expect everything to work. * It is source backwards compatible modulo major deprecation. Most of the API is here to stay forever, unless an unfixable security or design flaw is exposed. Minor releases never add source-incompatible changes to the stable API. #### Deprecation cycle When API is deprecated, it goes through multiple stages and there is at least one major release between each stages. 1. Feature is deprecated with compilation warning. Most of the time, proper replacement (and corresponding `replaceWith` declaration) is provided to automatically migrate deprecated usages with a help of IntelliJ IDEA. 2. Deprecation level is increased to error or hidden. It is no longer possible to compile new code against deprecated API, though it is still present in the ABI. 3. API is completely removed. While we give our best efforts not to do so and have no plans of removing any API, we still are leaving this option in case of unforeseen problems such as security issues. ### Experimental API This API marked as `@ExperimentalSerializationApi`. API is marked experimental when its design has potential open questions which may eventually lead to either semantics changes of the API or its deprecation. By default, most of the new API is marked as experimental and becomes stable in one of the next releases if no new issues arise. Otherwise, either semantics is fixed without changes in ABI or API goes through deprecation cycle. However, we'll try to provide best-effort compatibility — such declarations won't be deleted or changed instantly, they will go through deprecation cycle if this is possible. However, this deprecation cycle may be faster than usual. Usage notes: * Experimental API can be used in your applications if maintenance cost is clear: additional migrations may have to be performed during `kotlinx.serialization` update. * Experimental API can be used in other **experimental** API (for example, a custom serialization format). In such cases, clients of the API have to be aware about experimentality. * It's not recommended to use it as a dependency in your **stable** API, even as an implementation detail. Due to the lack of binary backward compatibility, your clients may experience behavioural changes or runtime exceptions when an unexpected version of `kotlinx.serialization` gets included in the runtime classpath. ### Internal API This API is marked with `@InternalSerializationApi` or located in `kotlinx.serialization.internal` package. It does not have any binary or source compatibility guarantees and can be deprecated or deleted without replacement at any time. It is not recommended to use it. However, if you have a rare use-case that can be solved only with internal API, it is possible to use it. In such a case, please create an issue on GitHub in order for us to understand a use-case and to provide stable alternative. ## Compatibility with Kotlin compiler plugin `kotlinx.serialization` also has the compiler plugin, that generates code depending on the core library. Therefore, the compiler plugin should be compatible with the runtime library to work. Kotlin & `kotlinx.serialization` plugin 1.4.0/1.4.10 are compatible with 1.0.0 runtime library. For further updates, we have the following policy: * New Kotlin compiler plugins should be backward compatible with core library. It means that it is possible to freely update Kotlin version in a project without changing the code and without the need to update `kotlinx.serialization` runtime. In other words, `1.0.0` runtime can be used with any of Kotlin `1.4.x` versions. * New Kotlin compiler plugin features may require new `kotlinx.serialization` library. For example, if Kotlin `1.4.x` gets serialization of unsigned integers, it would require a corresponding runtime version higher than `1.0.0`. This would be indicated by a compiler error specific to a particular feature. * New core library versions may or may not require Kotlin compiler plugin update, depending on a particular release. We'll try to avoid these situations; however, in case of some unexpected issues, it may be necessary. So it is possible to have a situation where upgrading serialization runtime from `1.a.0` to `1.b.0` requires an update of Kotlin version from `1.x.0` to `1.y.0` (`y` > `x`). The compiler can detect such problems and will inform you if its version is incompatible with a current version of core library. Note that according to [general rules for Kotlin binaries](https://kotlinlang.org/docs/kotlin-evolution-principles.html#compatibility-options), library is mostly forwards compatible with the next language release, but not later ones. It means that if kotlinx.serialization `1.x` was compiled with Kotlin `2.Y`, it is compatible with Kotlin `2.(Y + 1)`, but not `2.(Y + 2)`. --- ## File: docs/formats.md # Alternative and custom formats (experimental) This is the sixth chapter of the [Kotlin Serialization Guide](serialization-guide.md). It goes beyond JSON, covering alternative and custom formats. Unlike JSON, which is stable, these are currently experimental features of Kotlin Serialization. **Table of contents** * [CBOR (experimental)](#cbor-experimental) * [Ignoring unknown keys](#ignoring-unknown-keys) * [Byte arrays and CBOR data types](#byte-arrays-and-cbor-data-types) * [Definite vs. Indefinite Length Encoding](#definite-vs-indefinite-length-encoding) * [Tags and Labels](#tags-and-labels) * [Arrays](#arrays) * [Custom CBOR-specific Serializers](#custom-cbor-specific-serializers) * [ProtoBuf (experimental)](#protobuf-experimental) * [Field numbers](#field-numbers) * [Integer types](#integer-types) * [Lists as repeated fields](#lists-as-repeated-fields) * [Packed fields](#packed-fields) * [Oneof field (experimental)](#oneof-field-experimental) * [Usage](#usage) * [Alternative](#alternative) * [ProtoBuf schema generator (experimental)](#protobuf-schema-generator-experimental) * [Properties (experimental)](#properties-experimental) * [Custom formats (experimental)](#custom-formats-experimental) * [Basic encoder](#basic-encoder) * [Basic decoder](#basic-decoder) * [Sequential decoding](#sequential-decoding) * [Adding collection support](#adding-collection-support) * [Adding null support](#adding-null-support) * [Efficient binary format](#efficient-binary-format) * [Format-specific types](#format-specific-types) ## CBOR (experimental) [CBOR][RFC 8949] is one of the standard compact binary encodings for JSON, so it supports a subset of [JSON features](json.md) and is generally very similar to JSON in use, but produces binary data. > CBOR support is (experimentally) available in a separate > `org.jetbrains.kotlinx:kotlinx-serialization-cbor:` module. [Cbor] class has [Cbor.encodeToByteArray] and [Cbor.decodeFromByteArray] functions. Let us take the basic example from the [JSON encoding](basic-serialization.md#json-encoding), but encode it using CBOR. ```kotlin @Serializable data class Project(val name: String, val language: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", "Kotlin") val bytes = Cbor.encodeToByteArray(data) println(bytes.toAsciiHexString()) val obj = Cbor.decodeFromByteArray(bytes) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-01.kt). We print a filtered ASCII representation of the output, writing non-ASCII data in hex, so we see how all the original strings are directly represented in CBOR, but the format delimiters themselves are binary. ```text {BF}dnameukotlinx.serializationhlanguagefKotlin{FF} Project(name=kotlinx.serialization, language=Kotlin) ``` In [CBOR hex notation](http://cbor.me/), the output is equivalent to the following: ``` BF # map(*) 64 # text(4) 6E616D65 # "name" 75 # text(21) 6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization" 68 # text(8) 6C616E6775616765 # "language" 66 # text(6) 4B6F746C696E # "Kotlin" FF # primitive(*) ``` > Note, CBOR as a format, unlike JSON, supports maps with non-trivial keys > (see the [Allowing structured map keys](json.md#allowing-structured-map-keys) section for JSON workarounds), > and Kotlin maps are serialized as CBOR maps, but some parsers (like `jackson-dataformat-cbor`) don't support this. ### Ignoring unknown keys CBOR format is often used to communicate with [IoT] devices where new properties could be added as a part of a device's API evolution. By default, unknown keys encountered during deserialization produce an error. This behavior can be configured with the [ignoreUnknownKeys][CborBuilder.ignoreUnknownKeys] property. ```kotlin @Serializable data class Project(val name: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val format = Cbor { ignoreUnknownKeys = true } val data = format.decodeFromHexString( "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff" ) println(data) } ``` > You can get the full code [here](../guide/example/example-formats-02.kt). It decodes the object, despite the fact that `Project` is missing the `language` property. ```text Project(name=kotlinx.serialization) ``` In [CBOR hex notation](http://cbor.me/), the input is equivalent to the following: ``` BF # map(*) 64 # text(4) 6E616D65 # "name" 75 # text(21) 6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization" 68 # text(8) 6C616E6775616765 # "language" 66 # text(6) 4B6F746C696E # "Kotlin" FF # primitive(*) ``` ### Byte arrays and CBOR data types Per the [RFC 8949 Major Types] section, CBOR supports the following data types: - Major type 0: an unsigned integer - Major type 1: a negative integer - **Major type 2: a byte string** - Major type 3: a text string - **Major type 4: an array of data items** - Major type 5: a map of pairs of data items - Major type 6: optional semantic tagging of other major types - Major type 7: floating-point numbers and simple data types that need no content, as well as the "break" stop code By default, Kotlin `ByteArray` instances are encoded as **major type 4**. When **major type 2** is desired, then the [`@ByteString`][ByteString] annotation can be used. Moreover, the `alwaysUseByteString` configuration switch allows for globally preferring **major type 2** without needing to annotate every `ByteArray` in a class hierarchy. ```kotlin @Serializable @OptIn(ExperimentalSerializationApi::class) data class Data( @ByteString val type2: ByteArray, // CBOR Major type 2 val type4: ByteArray // CBOR Major type 4 ) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8)) val bytes = Cbor.encodeToByteArray(data) println(bytes.toAsciiHexString()) val obj = Cbor.decodeFromByteArray(bytes) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-03.kt). As we see, the CBOR byte that precedes the data is different for different types of encoding. ```text {BF}etype2D{01}{02}{03}{04}etype4{9F}{05}{06}{07}{08}{FF}{FF} Data(type2=[1, 2, 3, 4], type4=[5, 6, 7, 8]) ``` In [CBOR hex notation](http://cbor.me/), the output is equivalent to the following: ``` BF # map(*) 65 # text(5) 7479706532 # "type2" 44 # bytes(4) 01020304 # "\x01\x02\x03\x04" 65 # text(5) 7479706534 # "type4" 9F # array(*) 05 # unsigned(5) 06 # unsigned(6) 07 # unsigned(7) 08 # unsigned(8) FF # primitive(*) FF # primitive(*) ``` ### Definite vs. Indefinite Length Encoding CBOR supports two encodings for maps and arrays: definite and indefinite length encoding. kotlinx.serialization defaults to the latter, which means that a map's or array's number of elements is not encoded, but instead a terminating byte is appended after the last element. Definite length encoding, on the other hand, omits this terminating byte, but instead prepends number of elements to the contents of a map or array. The `useDefiniteLengthEncoding` configuration switch allows for toggling between the two modes of encoding. ### Tags and Labels CBOR allows for optionally defining *tags* for properties and their values. These tags are encoded into the resulting byte string to transport additional information (see [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info). The [`@KeyTags`](Tags.kt) and [`@ValueTags`](Tags.kt) annotations can be used to define such tags while writing and verifying such tags can be toggled using the `encodeKeyTags`, `encodeValueTags`, `verifyKeyTags`, and `verifyValueTags` configuration switches respectively. In addition, it is possible to directly declare classes to always be tagged. This then applies to all instances of such a tagged class, regardless of whether they are used as values in a list or when they are used as a property in another class. Forcing objects to always be tagged in such a manner is accomplished by the [`@ObjectTags`](Tags.kt) annotation, which works just as `ValueTags`, but for class definitions. When serializing, `ObjectTags` will always be encoded directly before to the data of the tagged object, i.e. a value-tagged property of an object-tagged type will have the value tags preceding the object tags. Writing and verifying object tags can be toggled using the `encodeObjectTags` and `verifyObjectTags` configuration switches. Note that verifying only value tags can result in some data with superfluous tags to still deserialize successfully, since in this case - by definition - only a partial validation of tags happens. Well-known tags are specified in [`CborTag`](Tags.kt). In addition, CBOR supports keys of all types which work just as `SerialName`s. COSE restricts this again to strings and numbers and calls these restricted map keys *labels*. String labels can be assigned by using `@SerialName`, while number labels can be assigned using the [`@CborLabel`](CborLabel.kt) annotation. The `preferCborLabelsOverNames` configuration switch can be used to prefer number labels over SerialNames in case both are present for a property. This duality allows for compact representation of a type when serialized to CBOR, while keeping expressive diagnostic names when serializing to JSON. A predefined Cbor instance (in addition to the default [`Cbor.Default`](Cbor.kt) one) is available, adhering to COSE encoding requirements as [`Cbor.CoseCompliant`](Cbor.kt). This instance uses definite length encoding, encodes and verifies all tags and prefers labels to serial names. ### Arrays Classes may be serialized as a CBOR Array (major type 4) instead of a CBOR Map (major type 5). Example usage: ``` @Serializable data class DataClass( val alg: Int, val kid: String? ) Cbor.encodeToByteArray(DataClass(alg = -7, kid = null)) ``` will normally produce a Cbor map: bytes `0xa263616c6726636b6964f6`, or in diagnostic notation: ``` A2 # map(2) 63 # text(3) 616C67 # "alg" 26 # negative(6) 63 # text(3) 6B6964 # "kid" F6 # primitive(22) ``` When annotated with `@CborArray`, serialization of the same object will produce a Cbor array: bytes `0x8226F6`, or in diagnostic notation: ``` 82 # array(2) 26 # negative(6) F6 # primitive(22) ``` This may be used to encode COSE structures, see [RFC 9052 2. Basic COSE Structure](https://www.rfc-editor.org/rfc/rfc9052#section-2). ### Custom CBOR-specific Serializers Cbor encoders and decoders implement the interfaces [CborEncoder](CborEncoder.kt) and [CborDecoder](CborDecoder.kt), respectively. These interfaces contain a single property, `cbor`, exposing the current CBOR serialization configuration. This enables custom cbor-specific serializers to reuse the current `Cbor` instance to produce embedded byte arrays or react to configuration settings such as `preferCborLabelsOverNames` or `useDefiniteLengthEncoding`, for example. ## ProtoBuf (experimental) [Protocol Buffers](https://developers.google.com/protocol-buffers) is a language-neutral binary format that normally relies on a separate ".proto" file that defines the protocol schema. It is more compact than CBOR, because it assigns integer numbers to fields instead of names. > Protocol buffers support is (experimentally) available in a separate > `org.jetbrains.kotlinx:kotlinx-serialization-protobuf:` module. Kotlin Serialization is using proto2 semantics, where all fields are explicitly required or optional. For a basic example we change our example to use the [ProtoBuf] class with [ProtoBuf.encodeToByteArray] and [ProtoBuf.decodeFromByteArray] functions. ```kotlin @Serializable data class Project(val name: String, val language: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", "Kotlin") val bytes = ProtoBuf.encodeToByteArray(data) println(bytes.toAsciiHexString()) val obj = ProtoBuf.decodeFromByteArray(bytes) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-04.kt). ```text {0A}{15}kotlinx.serialization{12}{06}Kotlin Project(name=kotlinx.serialization, language=Kotlin) ``` In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode), the output is equivalent to the following: ``` Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization" Field #2: 12 String Length = 6, Hex = 06, UTF8 = "Kotlin" ``` ### Field numbers By default, field numbers in the Kotlin Serialization [ProtoBuf] implementation are automatically assigned, which does not provide the ability to define a stable data schema that evolves over time. That is normally achieved by writing a separate ".proto" file. However, with Kotlin Serialization we can get this ability without a separate schema file, instead using the [ProtoNumber] annotation. ```kotlin @OptIn(ExperimentalSerializationApi::class) @Serializable data class Project( @ProtoNumber(1) val name: String, @ProtoNumber(3) val language: String ) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", "Kotlin") val bytes = ProtoBuf.encodeToByteArray(data) println(bytes.toAsciiHexString()) val obj = ProtoBuf.decodeFromByteArray(bytes) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-05.kt). We see in the output that the number for the first property `name` did not change (as it is numbered from one by default), but it did change for the `language` property. ```text {0A}{15}kotlinx.serialization{1A}{06}Kotlin Project(name=kotlinx.serialization, language=Kotlin) ``` In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode), the output is equivalent to the following: ``` Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization" (total 21 chars) Field #3: 1A String Length = 6, Hex = 06, UTF8 = "Kotlin" ``` ### Integer types Protocol buffers support various integer encodings optimized for different ranges of integers. They are specified using the [ProtoType] annotation and the [ProtoIntegerType] enum. The following example shows all three supported options. ```kotlin @OptIn(ExperimentalSerializationApi::class) @Serializable class Data( @ProtoType(ProtoIntegerType.DEFAULT) val a: Int, @ProtoType(ProtoIntegerType.SIGNED) val b: Int, @ProtoType(ProtoIntegerType.FIXED) val c: Int ) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Data(1, -2, 3) println(ProtoBuf.encodeToByteArray(data).toAsciiHexString()) } ``` > You can get the full code [here](../guide/example/example-formats-06.kt). * The [default][ProtoIntegerType.DEFAULT] is a varint encoding (`intXX`) that is optimized for small non-negative numbers. The value of `1` is encoded in one byte `01`. * The [signed][ProtoIntegerType.SIGNED] is a signed ZigZag encoding (`sintXX`) that is optimized for small signed integers. The value of `-2` is encoded in one byte `03`. * The [fixed][ProtoIntegerType.FIXED] encoding (`fixedXX`) always uses a fixed number of bytes. The value of `3` is encoded as four bytes `03 00 00 00`. > `uintXX` and `sfixedXX` protocol buffer types are not supported. ```text {08}{01}{10}{03}{1D}{03}{00}{00}{00} ``` In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode) the output is equivalent to the following: ``` Field #1: 08 Varint Value = 1, Hex = 01 Field #2: 10 Varint Value = 3, Hex = 03 Field #3: 1D Fixed32 Value = 3, Hex = 03-00-00-00 ``` ### Lists as repeated fields By default, kotlin lists and other collections are representend as repeated fields. In the protocol buffers when the list is empty there are no elements in the stream with the corresponding number. For Kotlin Serialization you must explicitly specify a default of `emptyList()` for any property of a collection or map type. Otherwise you will not be able deserialize an empty list, which is indistinguishable in protocol buffers from a missing field. ```kotlin @Serializable data class Data( val a: List = emptyList(), val b: List = emptyList() ) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Data(listOf(1, 2, 3), listOf()) val bytes = ProtoBuf.encodeToByteArray(data) println(bytes.toAsciiHexString()) println(ProtoBuf.decodeFromByteArray(bytes)) } ``` > You can get the full code [here](../guide/example/example-formats-07.kt). ```text {08}{01}{08}{02}{08}{03} Data(a=[1, 2, 3], b=[]) ``` In [ProtoBuf diagnostic mode](https://protogen.marcgravell.com/decode) the output is equivalent to the following: ``` Field #1: 08 Varint Value = 1, Hex = 01 Field #1: 08 Varint Value = 2, Hex = 02 Field #1: 08 Varint Value = 3, Hex = 03 ``` ### Packed fields Collection types (not maps) can be **written** as packed fields when annotated with the `@ProtoPacked` annotation. Per the standard packed fields can only be used on primitive numeric types. The annotation is ignored on other types. Per the [format description](https://developers.google.com/protocol-buffers/docs/encoding#packed) the parser ignores the annotation, but rather reads list in either packed or repeated format. ### Oneof field (experimental) Kotlin Serialization `ProtoBuf` format supports [oneof](https://protobuf.dev/programming-guides/proto2/#oneof) fields basing on the [Polymorphism](polymorphism.md) functionality. #### Usage Given a protobuf message defined like: ```proto message Data { required string name = 1; oneof phone { string home_phone = 2; string work_phone = 3; } } ``` You can define a kotlin class semantically equal to this message by following these steps: * Declare a sealed interface or abstract class, to represent of the `oneof` group, called *the oneof interface*. In our example, oneof interface is `IPhoneType`. * Declare a Kotlin class as usual to represent the whole message (`class Data` in our example). In this class, add the property with oneof interface type, annotated with `@ProtoOneOf`. Do not use `@ProtoNumber` for that property. * Declare subclasses for oneof interface, one per each oneof group element. Each class must have **exactly one property** with the corresponding oneof element type. In our example, these classes are `HomePhone` and `WorkPhone`. * Annotate properties in subclasses with `@ProtoNumber`, according to original `oneof` definition. In our example, `val number: String` in `HomePhone` has `@ProtoNumber(2)` annotation, because of field `string home_phone = 2;` in `oneof phone`. ```kotlin // The outer class @OptIn(ExperimentalSerializationApi::class) @Serializable data class Data( @ProtoNumber(1) val name: String, @ProtoOneOf val phone: IPhoneType?, ) // The oneof interface @Serializable sealed interface IPhoneType // Message holder for home_phone @OptIn(ExperimentalSerializationApi::class) @Serializable @JvmInline value class HomePhone(@ProtoNumber(2) val number: String): IPhoneType // Message holder for work_phone. Can also be a value class, but we leave it as `data` to demonstrate that both variants can be used. @OptIn(ExperimentalSerializationApi::class) @Serializable data class WorkPhone(@ProtoNumber(3) val number: String): IPhoneType @OptIn(ExperimentalSerializationApi::class) fun main() { val dataTom = Data("Tom", HomePhone("123")) val stringTom = ProtoBuf.encodeToHexString(dataTom) val dataJerry = Data("Jerry", WorkPhone("789")) val stringJerry = ProtoBuf.encodeToHexString(dataJerry) println(stringTom) println(stringJerry) println(ProtoBuf.decodeFromHexString(stringTom)) println(ProtoBuf.decodeFromHexString(stringJerry)) } ``` > You can get the full code [here](../guide/example/example-formats-08.kt). ```text 0a03546f6d1203313233 0a054a657272791a03373839 Data(name=Tom, phone=HomePhone(number=123)) Data(name=Jerry, phone=WorkPhone(number=789)) ``` In [ProtoBuf diagnostic mode](https://protogen.marcgravell.com/decode) the first 2 lines in the output are equivalent to ``` Field #1: 0A String Length = 3, Hex = 03, UTF8 = "Tom" Field #2: 12 String Length = 3, Hex = 03, UTF8 = "123" Field #1: 0A String Length = 5, Hex = 05, UTF8 = "Jerry" Field #3: 1A String Length = 3, Hex = 03, UTF8 = "789" ``` You should note that each group of `oneof` types should be tied to exactly one data class, and it is better not to reuse it in another data class. Otherwise, you may get id conflicts or `IllegalArgumentException` in runtime. #### Alternative You don't always need to apply the `@ProtoOneOf` form in your class for messages with `oneof` fields, if this class is only used for deserialization. For example, the following class: ``` @Serializable data class Data2( @ProtoNumber(1) val name: String, @ProtoNumber(2) val homeNumber: String? = null, @ProtoNumber(3) val workNumber: String? = null, ) ``` is also compatible with the `message Data` given above, which means the same input can be deserialized into it instead of `Data` — in case you don't want to deal with sealed hierarchies. But please note that there are no exclusivity checks. This means that if an instance of `Data2` has both (or none) `homeNumber` and `workNumber` as non-null values and is serialized to protobuf, it no longer complies with the original schema. If you send such data to another parser, one of the fields may be omitted, leading to an unknown issue. ### ProtoBuf schema generator (experimental) As mentioned above, when working with protocol buffers you usually use a ".proto" file and a code generator for your language. This includes the code to serialize your message to an output stream and deserialize it from an input stream. When using Kotlin Serialization this step is not necessary because your `@Serializable` Kotlin data types are used as the source for the schema. This is very convenient for Kotlin-to-Kotlin communication, but makes interoperability between languages complicated. Fortunately, you can use the ProtoBuf schema generator to output the ".proto" representation of your messages. You can keep your Kotlin classes as a source of truth and use traditional protoc compilers for other languages at the same time. As an example, we can display the following data class's ".proto" schema as follows. ```kotlin @Serializable data class SampleData( val amount: Long, val description: String?, val department: String = "QA" ) @OptIn(ExperimentalSerializationApi::class) fun main() { val descriptors = listOf(SampleData.serializer().descriptor) val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors) println(schemas) } ``` > You can get the full code [here](../guide/example/example-formats-09.kt). Which would output as follows. ```text syntax = "proto2"; // serial name 'example.exampleFormats09.SampleData' message SampleData { required int64 amount = 1; optional string description = 2; // WARNING: a default value decoded when value is missing optional string department = 3; } ``` Note that since default values are not represented in ".proto" files, a warning is generated when one appears in the schema. See the documentation for [ProtoBufSchemaGenerator] for more information. ## Properties (experimental) Kotlin Serialization can serialize a class into a flat map with `String` keys via the [Properties][kotlinx.serialization.properties.Properties] format implementation. > Properties support is (experimentally) available in a separate > `org.jetbrains.kotlinx:kotlinx-serialization-properties:` module. ```kotlin @Serializable class Project(val name: String, val owner: User) @Serializable class User(val name: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", User("kotlin")) val map = Properties.encodeToMap(data) map.forEach { (k, v) -> println("$k = $v") } } ``` > You can get the full code [here](../guide/example/example-formats-10.kt). The resulting map has dot-separated keys representing keys of the nested objects. ```text name = kotlinx.serialization owner.name = kotlin ``` ## Custom formats (experimental) A custom format for Kotlin Serialization must provide an implementation for the [Encoder] and [Decoder] interfaces that we saw used in the [Serializers](serializers.md) chapter. These are pretty large interfaces. For convenience the [AbstractEncoder] and [AbstractDecoder] skeleton implementations are provided to simplify the task. In [AbstractEncoder] most of the `encodeXxx` methods have a default implementation that delegates to [`encodeValue(value: Any)`][AbstractEncoder.encodeValue] — the only method that must be implemented to get a basic working format. ### Basic encoder Let us start with a trivial format implementation that encodes the data into a single list of primitive constituent objects in the order they were written in the source code. To start, we implement a simple [Encoder] by overriding `encodeValue` in [AbstractEncoder]. Since encoders are intended to be consumed by other parts of application, it is recommended to propagate the `@ExperimentalSerializationApi` annotation instead of opting-in. ```kotlin @ExperimentalSerializationApi class ListEncoder : AbstractEncoder() { val list = mutableListOf() override val serializersModule: SerializersModule = EmptySerializersModule() override fun encodeValue(value: Any) { list.add(value) } } ``` Now we write a convenience top-level function that creates an encoder that encodes an object and returns a list. ```kotlin @ExperimentalSerializationApi fun encodeToList(serializer: SerializationStrategy, value: T): List { val encoder = ListEncoder() encoder.encodeSerializableValue(serializer, value) return encoder.list } ``` For even more convenience, to avoid the need to explicitly pass a serializer, we write an `inline` overload of the `encodeToList` function with a `reified` type parameter using the [serializer] function to retrieve the appropriate [KSerializer] instance for the actual type. ```kotlin @ExperimentalSerializationApi inline fun encodeToList(value: T) = encodeToList(serializer(), value) ``` Now we can test it. ```kotlin @Serializable data class Project(val name: String, val owner: User, val votes: Int) @Serializable data class User(val name: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", User("kotlin"), 9000) println(encodeToList(data)) } ``` > You can get the full code [here](../guide/example/example-formats-11.kt). As a result, we got all the primitive values in our object graph visited and put into a list in _serial_ order. ```text [kotlinx.serialization, kotlin, 9000] ``` > By itself, that's a useful feature if we need compute some kind of hashcode or digest for all the data > that is contained in a serializable object tree. ### Basic decoder A decoder needs to implement more substance. * [decodeValue][AbstractDecoder.decodeValue] — returns the next value from the list. * [decodeElementIndex][CompositeDecoder.decodeElementIndex] — returns the next index of a deserialized value. In this primitive format deserialization always happens in order, so we keep track of the index in the `elementIndex` variable. See the [Hand-written composite serializer](serializers.md#hand-written-composite-serializer) section on how it ends up being used. * [beginStructure][Decoder.beginStructure] — returns a new instance of `ListDecoder`, so that each structure that is being recursively decoded keeps track of its own `elementIndex` state separately. ```kotlin @ExperimentalSerializationApi class ListDecoder(val list: ArrayDeque) : AbstractDecoder() { private var elementIndex = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun decodeValue(): Any = list.removeFirst() override fun decodeElementIndex(descriptor: SerialDescriptor): Int { if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE return elementIndex++ } override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = ListDecoder(list) } ``` A couple of convenience functions for decoding. ```kotlin @ExperimentalSerializationApi fun decodeFromList(list: List, deserializer: DeserializationStrategy): T { val decoder = ListDecoder(ArrayDeque(list)) return decoder.decodeSerializableValue(deserializer) } @ExperimentalSerializationApi inline fun decodeFromList(list: List): T = decodeFromList(list, serializer()) ``` That is enough to start encoding and decoding basic serializable classes. ```kotlin @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", User("kotlin"), 9000) val list = encodeToList(data) println(list) val obj = decodeFromList(list) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-12.kt). Now we can convert a list of primitives back to an object tree. ```text [kotlinx.serialization, kotlin, 9000] Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000) ``` ### Sequential decoding The decoder we have implemented keeps track of the `elementIndex` in its state and implements `decodeElementIndex`. This means that it is going to work with an arbitrary serializer, even the simple one we wrote in the [Hand-written composite serializer](serializers.md#hand-written-composite-serializer) section. However, this format always stores elements in order, so this bookkeeping is not needed and undermines decoding performance. All auto-generated serializers on the JVM support the [Sequential decoding protocol (experimental)](serializers.md#sequential-decoding-protocol-experimental), and the decoder can indicate its support by returning `true` from the [CompositeDecoder.decodeSequentially] function. ```kotlin @ExperimentalSerializationApi class ListDecoder(val list: ArrayDeque) : AbstractDecoder() { private var elementIndex = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun decodeValue(): Any = list.removeFirst() override fun decodeElementIndex(descriptor: SerialDescriptor): Int { if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE return elementIndex++ } override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = ListDecoder(list) override fun decodeSequentially(): Boolean = true } ``` > You can get the full code [here](../guide/example/example-formats-13.kt). ### Adding collection support This basic format, so far, cannot properly represent collections. In encodes them, but it does not keep track of how many elements there are in the collection or where it ends, so it cannot properly decode them. First, let us add proper support for collections to the encoder by implementing the [Encoder.beginCollection] function. The `beginCollection` function takes a collection size as a parameter, so we encode it to add it to the result. Our encoder implementation does not keep any state, so it just returns `this` from the `beginCollection` function. ```kotlin @ExperimentalSerializationApi class ListEncoder : AbstractEncoder() { val list = mutableListOf() override val serializersModule: SerializersModule = EmptySerializersModule() override fun encodeValue(value: Any) { list.add(value) } override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder { encodeInt(collectionSize) return this } } ``` The decoder, for our case, needs to only implement the [CompositeDecoder.decodeCollectionSize] function in addition to the previous code. > The formats that store collection size in advance have to return `true` from `decodeSequentially`. ```kotlin @ExperimentalSerializationApi class ListDecoder(val list: ArrayDeque, var elementsCount: Int = 0) : AbstractDecoder() { private var elementIndex = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun decodeValue(): Any = list.removeFirst() override fun decodeElementIndex(descriptor: SerialDescriptor): Int { if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE return elementIndex++ } override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = ListDecoder(list, descriptor.elementsCount) override fun decodeSequentially(): Boolean = true override fun decodeCollectionSize(descriptor: SerialDescriptor): Int = decodeInt().also { elementsCount = it } } ``` That is all that is needed to support collections and maps. ```kotlin @Serializable data class Project(val name: String, val owners: List, val votes: Int) @Serializable data class User(val name: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", listOf(User("kotlin"), User("jetbrains")), 9000) val list = encodeToList(data) println(list) val obj = decodeFromList(list) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-14.kt). We see the size of the list added to the result, letting the decoder know where to stop. ```text [kotlinx.serialization, 2, kotlin, jetbrains, 9000] Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000) ``` ### Adding null support Our trivial format does not support `null` values so far. For nullable types we need to add some kind of "null indicator", telling whether the upcoming value is null or not. In the encoder implementation we override [Encoder.encodeNull] and [Encoder.encodeNotNullMark]. ```kotlin override fun encodeNull() = encodeValue("NULL") override fun encodeNotNullMark() = encodeValue("!!") ``` In the decoder implementation we override [Decoder.decodeNotNullMark]. ```kotlin override fun decodeNotNullMark(): Boolean = decodeString() != "NULL" ``` Let us test nullable properties both with not-null and null values. ```kotlin @Serializable data class Project(val name: String, val owner: User?, val votes: Int?) @Serializable data class User(val name: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", User("kotlin") , null) val list = encodeToList(data) println(list) val obj = decodeFromList(list) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-15.kt). In the output we see how not-null`!!` and `NULL` marks are used. ```text [kotlinx.serialization, !!, kotlin, NULL] Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null) ``` ### Efficient binary format Now we are ready for an example of an efficient binary format. We are going to write data to the [java.io.DataOutput] implementation. Instead of `encodeValue` we must override the individual `encodeXxx` functions for each of ten [primitives](builtin-classes.md#primitives) in the encoder. ```kotlin @ExperimentalSerializationApi class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() { override val serializersModule: SerializersModule = EmptySerializersModule() override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0) override fun encodeByte(value: Byte) = output.writeByte(value.toInt()) override fun encodeShort(value: Short) = output.writeShort(value.toInt()) override fun encodeInt(value: Int) = output.writeInt(value) override fun encodeLong(value: Long) = output.writeLong(value) override fun encodeFloat(value: Float) = output.writeFloat(value) override fun encodeDouble(value: Double) = output.writeDouble(value) override fun encodeChar(value: Char) = output.writeChar(value.code) override fun encodeString(value: String) = output.writeUTF(value) override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index) override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder { encodeInt(collectionSize) return this } override fun encodeNull() = encodeBoolean(false) override fun encodeNotNullMark() = encodeBoolean(true) } ``` The decoder implementation mirrors encoder's implementation overriding all the primitive `decodeXxx` functions. ```kotlin @ExperimentalSerializationApi class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() { private var elementIndex = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0 override fun decodeByte(): Byte = input.readByte() override fun decodeShort(): Short = input.readShort() override fun decodeInt(): Int = input.readInt() override fun decodeLong(): Long = input.readLong() override fun decodeFloat(): Float = input.readFloat() override fun decodeDouble(): Double = input.readDouble() override fun decodeChar(): Char = input.readChar() override fun decodeString(): String = input.readUTF() override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt() override fun decodeElementIndex(descriptor: SerialDescriptor): Int { if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE return elementIndex++ } override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = DataInputDecoder(input, descriptor.elementsCount) override fun decodeSequentially(): Boolean = true override fun decodeCollectionSize(descriptor: SerialDescriptor): Int = decodeInt().also { elementsCount = it } override fun decodeNotNullMark(): Boolean = decodeBoolean() } ``` We can now serialize and deserialize arbitrary data. For example, the same classes as were used in the [CBOR (experimental)](#cbor-experimental) and [ProtoBuf (experimental)](#protobuf-experimental) sections. ```kotlin @Serializable data class Project(val name: String, val language: String) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", "Kotlin") val output = ByteArrayOutputStream() encodeTo(DataOutputStream(output), data) val bytes = output.toByteArray() println(bytes.toAsciiHexString()) val input = ByteArrayInputStream(bytes) val obj = decodeFrom(DataInputStream(input)) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-16.kt). As we can see, the result is a dense binary format that only contains the data that is being serialized. It can be easily tweaked for any kind of domain-specific compact encoding. ```text {00}{15}kotlinx.serialization{00}{06}Kotlin Project(name=kotlinx.serialization, language=Kotlin) ``` ### Format-specific types A format implementation might provide special support for data types that are not among the list of primitive types in Kotlin Serialization, and do not have a corresponding `encodeXxx`/`decodeXxx` function. In the encoder this is achieved by overriding the [`encodeSerializableValue(serializer, value)`][Encoder.encodeSerializableValue] function. In our `DataOutput` format example we might want to provide a specialized efficient data path for serializing an array of bytes since [DataOutput][java.io.DataOutput] has a special method for this purpose. Detection of the type is performed by looking at the `serializer.descriptor`, not by checking the type of the `value` being serialized, so we fetch the builtin [KSerializer] instance for `ByteArray` type. > This an important difference. This way our format implementation properly supports > [Custom serializers](serializers.md#custom-serializers) that a user might specify for a type that just happens > to be internally represented as a byte array, but need a different serial representation. ```kotlin private val byteArraySerializer = serializer() ``` > Specifically for byte arrays, we could have also used the builtin > [ByteArraySerializer][kotlinx.serialization.builtins.ByteArraySerializer()] function. We add the corresponding code to the [Encoder] implementation of our [Efficient binary format](#efficient-binary-format). To make our `ByteArray` encoding even more efficient, we add a trivial implementation of `encodeCompactSize` function that uses only one byte to represent a size of up to 254 bytes. ```kotlin override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) { if (serializer.descriptor == byteArraySerializer.descriptor) encodeByteArray(value as ByteArray) else super.encodeSerializableValue(serializer, value) } private fun encodeByteArray(bytes: ByteArray) { encodeCompactSize(bytes.size) output.write(bytes) } private fun encodeCompactSize(value: Int) { if (value < 0xff) { output.writeByte(value) } else { output.writeByte(0xff) output.writeInt(value) } } ``` A similar code is added to the [Decoder] implementation. Here we override the [decodeSerializableValue][Decoder.decodeSerializableValue] function. ```kotlin @Suppress("UNCHECKED_CAST") override fun decodeSerializableValue(deserializer: DeserializationStrategy, previousValue: T?): T = if (deserializer.descriptor == byteArraySerializer.descriptor) decodeByteArray() as T else super.decodeSerializableValue(deserializer, previousValue) private fun decodeByteArray(): ByteArray { val bytes = ByteArray(decodeCompactSize()) input.readFully(bytes) return bytes } private fun decodeCompactSize(): Int { val byte = input.readByte().toInt() and 0xff if (byte < 0xff) return byte return input.readInt() } ``` Now everything is ready to perform serialization of some byte arrays. ```kotlin @Serializable data class Project(val name: String, val attachment: ByteArray) @OptIn(ExperimentalSerializationApi::class) fun main() { val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D)) val output = ByteArrayOutputStream() encodeTo(DataOutputStream(output), data) val bytes = output.toByteArray() println(bytes.toAsciiHexString()) val input = ByteArrayInputStream(bytes) val obj = decodeFrom(DataInputStream(input)) println(obj) } ``` > You can get the full code [here](../guide/example/example-formats-17.kt). As we can see, our custom byte array format is being used, with the compact encoding of its size in one byte. ```text {00}{15}kotlinx.serialization{04}{0A}{0B}{0C}{0D} Project(name=kotlinx.serialization, attachment=[10, 11, 12, 13]) ``` --- This chapter concludes [Kotlin Serialization Guide](serialization-guide.md). [RFC 8949]: https://tools.ietf.org/html/rfc8949 [IoT]: https://en.wikipedia.org/wiki/Internet_of_things [RFC 8949 Major Types]: https://tools.ietf.org/html/rfc8949#section-3.1 [java.io.DataOutput]: https://docs.oracle.com/javase/8/docs/api/java/io/DataOutput.html [serializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/serializer.html [KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html [kotlinx.serialization.builtins.ByteArraySerializer()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-byte-array-serializer.html [Encoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html [Decoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html [AbstractEncoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/index.html [AbstractDecoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/index.html [AbstractEncoder.encodeValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/encode-value.html [AbstractDecoder.decodeValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/decode-value.html [CompositeDecoder.decodeElementIndex]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-element-index.html [Decoder.beginStructure]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/begin-structure.html [CompositeDecoder.decodeSequentially]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-sequentially.html [Encoder.beginCollection]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-collection.html [CompositeDecoder.decodeCollectionSize]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-collection-size.html [Encoder.encodeNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-null.html [Encoder.encodeNotNullMark]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-not-null-mark.html [Decoder.decodeNotNullMark]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-not-null-mark.html [Encoder.encodeSerializableValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-serializable-value.html [Decoder.decodeSerializableValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html [kotlinx.serialization.properties.Properties]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-properties/kotlinx.serialization.properties/-properties/index.html [ProtoBuf]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/index.html [ProtoBuf.encodeToByteArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/encode-to-byte-array.html [ProtoBuf.decodeFromByteArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/decode-from-byte-array.html [ProtoNumber]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-number/index.html [ProtoType]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-type/index.html [ProtoIntegerType]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/index.html [ProtoIntegerType.DEFAULT]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-d-e-f-a-u-l-t/index.html [ProtoIntegerType.SIGNED]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-s-i-g-n-e-d/index.html [ProtoIntegerType.FIXED]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-f-i-x-e-d/index.html [ProtoBufSchemaGenerator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf.schema/-proto-buf-schema-generator/index.html [Cbor]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/index.html [Cbor.encodeToByteArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/encode-to-byte-array.html [Cbor.decodeFromByteArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/decode-from-byte-array.html [CborBuilder.ignoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor-builder/ignore-unknown-keys.html [ByteString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-byte-string/index.html --- ## File: docs/inline-classes.md The documentation has been moved to the [value-classes.md](value-classes.md) page. --- ## File: docs/json.md # JSON features This is the fifth chapter of the [Kotlin Serialization Guide](serialization-guide.md). In this chapter, we'll walk through features of [JSON](https://www.json.org/json-en.html) serialization available in the [Json] class. **Table of contents** * [Json configuration](#json-configuration) * [Pretty printing](#pretty-printing) * [Lenient parsing](#lenient-parsing) * [Ignoring unknown keys](#ignoring-unknown-keys) * [Ignoring unknown keys per class](#ignoring-unknown-keys-per-class) * [Alternative Json names](#alternative-json-names) * [Encoding defaults](#encoding-defaults) * [Explicit nulls](#explicit-nulls) * [Coercing input values](#coercing-input-values) * [Allowing structured map keys](#allowing-structured-map-keys) * [Allowing special floating-point values](#allowing-special-floating-point-values) * [Class discriminator for polymorphism](#class-discriminator-for-polymorphism) * [Class discriminator output mode](#class-discriminator-output-mode) * [Decoding enums in a case-insensitive manner](#decoding-enums-in-a-case-insensitive-manner) * [Global naming strategy](#global-naming-strategy) * [Base64](#base64) * [Json elements](#json-elements) * [Parsing to Json element](#parsing-to-json-element) * [Types of Json elements](#types-of-json-elements) * [Json element builders](#json-element-builders) * [Decoding Json elements](#decoding-json-elements) * [Encoding literal Json content](#encoding-literal-json-content) * [Serializing large decimal numbers](#serializing-large-decimal-numbers) * [Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden](#using-jsonunquotedliteral-to-create-a-literal-unquoted-value-of-null-is-forbidden) * [Json transformations](#json-transformations) * [Array wrapping](#array-wrapping) * [Array unwrapping](#array-unwrapping) * [Manipulating default values](#manipulating-default-values) * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization) * [Extending the behavior of the plugin generated serializer](#extending-the-behavior-of-the-plugin-generated-serializer) * [Under the hood (experimental)](#under-the-hood-experimental) * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes) ## Json configuration The default [Json] implementation is quite strict with respect to invalid inputs. It enforces Kotlin type safety and restricts Kotlin values that can be serialized so that the resulting JSON representations are standard. Many non-standard JSON features are supported by creating a custom instance of a JSON _format_. To use a custom JSON format configuration, create your own [Json] class instance from an existing instance, such as a default `Json` object, using the [Json()] builder function. Specify parameter values in the parentheses via the [JsonBuilder] DSL. The resulting `Json` format instance is immutable and thread-safe; it can be simply stored in a top-level property. > We recommend that you store and reuse custom instances of formats for performance reasons because format implementations > may cache format-specific additional information about the classes they serialize. This chapter shows configuration features that [Json] supports. ### Pretty printing By default, the [Json] output is a single line. You can configure it to pretty print the output (that is, add indentations and line breaks for better readability) by setting the [prettyPrint][JsonBuilder.prettyPrint] property to `true`: ```kotlin val format = Json { prettyPrint = true } @Serializable data class Project(val name: String, val language: String) fun main() { val data = Project("kotlinx.serialization", "Kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-01.kt). It gives the following nice result: ```text { "name": "kotlinx.serialization", "language": "Kotlin" } ``` ### Lenient parsing By default, [Json] parser enforces various JSON restrictions to be as specification-compliant as possible (see [RFC-4627]). Particularly, keys and string literals must be quoted. Those restrictions can be relaxed with the [isLenient][JsonBuilder.isLenient] property. With `isLenient = true`, you can parse quite freely-formatted data: ```kotlin val format = Json { isLenient = true } enum class Status { SUPPORTED } @Serializable data class Project(val name: String, val status: Status, val votes: Int) fun main() { val data = format.decodeFromString(""" { name : kotlinx.serialization, status : SUPPORTED, votes : "9000" } """) println(data) } ``` > You can get the full code [here](../guide/example/example-json-02.kt). You get the object, even though all keys of the source JSON, string and enum values are unquoted: ```text Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000) ``` > Note that parsing of quoted numbers or booleans such as `votes: "9000"` to `val votes: Int` is generally allowed by kotlinx.serialization > regardless of the `isLenient` flag, since such JSON is syntactically valid. ### Ignoring unknown keys JSON format is often used to read the output of third-party services or in other dynamic environments where new properties can be added during the API evolution. By default, unknown keys encountered during deserialization produce an error. You can avoid this and just ignore such keys by setting the [ignoreUnknownKeys][JsonBuilder.ignoreUnknownKeys] property to `true`: ```kotlin val format = Json { ignoreUnknownKeys = true } @Serializable data class Project(val name: String) fun main() { val data = format.decodeFromString(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) println(data) } ``` > You can get the full code [here](../guide/example/example-json-03.kt). It decodes the object despite the fact that the `Project` class doesn't have the `language` property: ```text Project(name=kotlinx.serialization) ``` ### Ignoring unknown keys per class Sometimes, for cleaner and safer API, it is desirable to ignore unknown properties only for specific classes. In that case, you can use [JsonIgnoreUnknownKeys] annotation on such classes while leaving global [ignoreUnknownKeys][JsonBuilder.ignoreUnknownKeys] setting turned off: ```kotlin @OptIn(ExperimentalSerializationApi::class) // JsonIgnoreUnknownKeys is an experimental annotation for now @Serializable @JsonIgnoreUnknownKeys data class Outer(val a: Int, val inner: Inner) @Serializable data class Inner(val x: String) fun main() { // 1 println(Json.decodeFromString("""{"a":1,"inner":{"x":"value"},"unknownKey":42}""")) println() // 2 println(Json.decodeFromString("""{"a":1,"inner":{"x":"value","unknownKey":"unknownValue"}}""")) } ``` > You can get the full code [here](../guide/example/example-json-04.kt). Line (1) decodes successfully despite "unknownKey" in `Outer`, because annotation is present on the class. However, line (2) throws `SerializationException` because there is no "unknownKey" property in `Inner`: ```text Outer(a=1, inner=Inner(x=value)) Exception in thread "main" kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 29: Encountered an unknown key 'unknownKey' at path: $.inner Use 'ignoreUnknownKeys = true' in 'Json {}' builder or '@JsonIgnoreUnknownKeys' annotation to ignore unknown keys. ``` ### Alternative Json names It's not a rare case when JSON fields are renamed due to a schema version change. You can use the [`@SerialName` annotation](basic-serialization.md#serial-field-names) to change the name of a JSON field, but such renaming blocks the ability to decode data with the old name. To support multiple JSON names for the one Kotlin property, there is the [JsonNames] annotation: ```kotlin @OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now @Serializable data class Project(@JsonNames("title") val name: String) fun main() { val project = Json.decodeFromString("""{"name":"kotlinx.serialization"}""") println(project) val oldProject = Json.decodeFromString("""{"title":"kotlinx.coroutines"}""") println(oldProject) } ``` > You can get the full code [here](../guide/example/example-json-05.kt). As you can see, both `name` and `title` Json fields correspond to `name` property: ```text Project(name=kotlinx.serialization) Project(name=kotlinx.coroutines) ``` Support for [JsonNames] annotation is controlled by the [JsonBuilder.useAlternativeNames] flag. Unlike most of the configuration flags, this one is enabled by default and does not need attention unless you want to do some fine-tuning. ### Encoding defaults Default values of properties are not encoded by default because they will be assigned to missing fields during decoding anyway. See the [Defaults are not encoded](basic-serialization.md#defaults-are-not-encoded-by-default) section for details and an example. This is especially useful for nullable properties with null defaults and avoids writing the corresponding null values. The default behavior can be changed by setting the [encodeDefaults][JsonBuilder.encodeDefaults] property to `true`: ```kotlin val format = Json { encodeDefaults = true } @Serializable class Project( val name: String, val language: String = "Kotlin", val website: String? = null ) fun main() { val data = Project("kotlinx.serialization") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-06.kt). It produces the following output which encodes all the property values including the default ones: ```text {"name":"kotlinx.serialization","language":"Kotlin","website":null} ``` ### Explicit nulls By default, all `null` values are encoded into JSON strings, but in some cases you may want to omit them. The encoding of `null` values can be controlled with the [explicitNulls][JsonBuilder.explicitNulls] property. If you set property to `false`, fields with `null` values are not encoded into JSON even if the property does not have a default `null` value. When decoding such JSON, the absence of a property value is treated as `null` for nullable properties without a default value. ```kotlin val format = Json { explicitNulls = false } @Serializable data class Project( val name: String, val language: String, val version: String? = "1.2.2", val website: String?, val description: String? = null ) fun main() { val data = Project("kotlinx.serialization", "Kotlin", null, null, null) val json = format.encodeToString(data) println(json) println(format.decodeFromString(json)) } ``` > You can get the full code [here](../guide/example/example-json-07.kt). As you can see, `version`, `website` and `description` fields are not present in output JSON on the first line. After decoding, the missing nullable property `website` without a default values has received a `null` value, while nullable properties `version` and `description` are filled with their default values: ```text {"name":"kotlinx.serialization","language":"Kotlin"} Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null) ``` > Pay attention to the fact that `version` was `null` before encoding and became `1.2.2` after decoding. > Encoding/decoding of properties like this — nullable with a non-null default — becomes asymmetrical if `explicitNulls` is set to `false`. It is possible to make the decoder treat some invalid input data as a missing field to enhance the functionality of this flag. See [coerceInputValues](#coercing-input-values) below for details. `explicitNulls` is `true` by default as it is the default behavior across different versions of the library. ### Coercing input values JSON formats that from third parties can evolve, sometimes changing the field types. This can lead to exceptions during decoding when the actual values do not match the expected values. The default [Json] implementation is strict with respect to input types as was demonstrated in the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction using the [coerceInputValues][JsonBuilder.coerceInputValues] property. This property only affects decoding. It treats a limited subset of invalid input values as if the corresponding property was missing. The current list of supported invalid values is: * `null` inputs for non-nullable types * unknown values for enums If value is missing, it is replaced either with a default property value if it exists, or with a `null` if [explicitNulls](#explicit-nulls) flag is set to `false` and a property is nullable (for enums). > This list may be expanded in the future, so that [Json] instance configured with this property becomes even more > permissive to invalid value in the input, replacing them with defaults or nulls. See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section: ```kotlin val format = Json { coerceInputValues = true } @Serializable data class Project(val name: String, val language: String = "Kotlin") fun main() { val data = format.decodeFromString(""" {"name":"kotlinx.serialization","language":null} """) println(data) } ``` > You can get the full code [here](../guide/example/example-json-08.kt). The invalid `null` value for the `language` property was coerced into the default value: ```text Project(name=kotlinx.serialization, language=Kotlin) ``` Example of using this flag together with [explicitNulls](#explicit-nulls) to coerce invalid enum values: ```kotlin enum class Color { BLACK, WHITE } @Serializable data class Brush(val foreground: Color = Color.BLACK, val background: Color?) val json = Json { coerceInputValues = true explicitNulls = false } fun main() { val brush = json.decodeFromString("""{"foreground":"pink", "background":"purple"}""") println(brush) } ``` > You can get the full code [here](../guide/example/example-json-09.kt). Despite that we do not have `Color.pink` and `Color.purple` colors, `decodeFromString` function returns successfully: ```text Brush(foreground=BLACK, background=null) ``` `foreground` property received its default value, and `background` property received `null` because of `explicitNulls = false` setting. ### Allowing structured map keys JSON format does not natively support the concept of a map with structured keys. Keys in JSON objects are strings and can be used to represent only primitives or enums by default. You can enable non-standard support for structured keys with the [allowStructuredMapKeys][JsonBuilder.allowStructuredMapKeys] property. This is how you can serialize a map with keys of a user-defined class: ```kotlin val format = Json { allowStructuredMapKeys = true } @Serializable data class Project(val name: String) fun main() { val map = mapOf( Project("kotlinx.serialization") to "Serialization", Project("kotlinx.coroutines") to "Coroutines" ) println(format.encodeToString(map)) } ``` > You can get the full code [here](../guide/example/example-json-10.kt). The map with structured keys gets represented as JSON array with the following items: `[key1, value1, key2, value2,...]`. ```text [{"name":"kotlinx.serialization"},"Serialization",{"name":"kotlinx.coroutines"},"Coroutines"] ``` ### Allowing special floating-point values By default, special floating-point values like [Double.NaN] and infinities are not supported in JSON because the JSON specification prohibits it. You can enable their encoding using the [allowSpecialFloatingPointValues][JsonBuilder.allowSpecialFloatingPointValues] property: ```kotlin val format = Json { allowSpecialFloatingPointValues = true } @Serializable class Data( val value: Double ) fun main() { val data = Data(Double.NaN) println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-11.kt). This example produces the following non-stardard JSON output, yet it is a widely used encoding for special values in JVM world: ```text {"value":NaN} ``` ### Class discriminator for polymorphism A key name that specifies a type when you have a polymorphic data can be specified in the [classDiscriminator][JsonBuilder.classDiscriminator] property: ```kotlin val format = Json { classDiscriminator = "#class" } @Serializable sealed class Project { abstract val name: String } @Serializable @SerialName("owned") class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-12.kt). In combination with an explicitly specified [SerialName] of the class it provides full control over the resulting JSON object: ```text {"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"} ``` It is also possible to specify different class discriminators for different hierarchies. Instead of Json instance property, use [JsonClassDiscriminator] annotation directly on base serializable class: ```kotlin @OptIn(ExperimentalSerializationApi::class) // JsonClassDiscriminator is an experimental annotation for now @Serializable @JsonClassDiscriminator("message_type") sealed class Base ``` This annotation is _inheritable_, so all subclasses of `Base` will have the same discriminator: ```kotlin @Serializable // Class discriminator is inherited from Base sealed class ErrorClass: Base() ``` > To learn more about inheritable serial annotations, see documentation for [InheritableSerialInfo]. Note that it is not possible to explicitly specify different class discriminators in subclasses of `Base`. Only hierarchies with empty intersections can have different discriminators. Discriminator specified in the annotation has priority over discriminator in Json configuration: ```kotlin val format = Json { classDiscriminator = "#class" } fun main() { val data = Message(BaseMessage("not found"), GenericError(404)) println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-13.kt). As you can see, discriminator from the `Base` class is used: ```text {"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}} ``` ### Class discriminator output mode Class discriminator provides information for serializing and deserializing [polymorphic class hierarchies](polymorphism.md#sealed-classes). As shown above, it is only added for polymorphic classes by default. In case you want to encode more or less information for various third party APIs about types in the output, it is possible to control addition of the class discriminator with the [JsonBuilder.classDiscriminatorMode] property. For example, [ClassDiscriminatorMode.NONE] does not add class discriminator at all, in case the receiving party is not interested in Kotlin types: ```kotlin @OptIn(ExperimentalSerializationApi::class) // classDiscriminatorMode is an experimental setting for now val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE } @Serializable sealed class Project { abstract val name: String } @Serializable class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-14.kt). Note that it would be impossible to deserialize this output back with kotlinx.serialization. ```text {"name":"kotlinx.coroutines","owner":"kotlin"} ``` Two other available values are [ClassDiscriminatorMode.POLYMORPHIC] (default behavior) and [ClassDiscriminatorMode.ALL_JSON_OBJECTS] (adds discriminator whenever possible). Consult their documentation for details. ### Decoding enums in a case-insensitive manner [Kotlin's naming policy recommends](https://kotlinlang.org/docs/coding-conventions.html#property-names) naming enum values using either uppercase underscore-separated names or upper camel case names. [Json] uses exact Kotlin enum values names for decoding by default. However, sometimes third-party JSONs have such values named in lowercase or some mixed case. In this case, it is possible to decode enum values in a case-insensitive manner using [JsonBuilder.decodeEnumsCaseInsensitive] property: ```kotlin @OptIn(ExperimentalSerializationApi::class) // decodeEnumsCaseInsensitive is an experimental setting for now val format = Json { decodeEnumsCaseInsensitive = true } @OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B } @Serializable data class CasesList(val cases: List) fun main() { println(format.decodeFromString("""{"cases":["value_A", "alternative"]}""")) } ``` > You can get the full code [here](../guide/example/example-json-15.kt). It affects serial names as well as alternative names specified with [JsonNames] annotation, so both values are successfully decoded: ```text CasesList(cases=[VALUE_A, VALUE_B]) ``` This property does not affect encoding in any way. ### Global naming strategy If properties' names in Json input are different from Kotlin ones, it is recommended to specify the name for each property explicitly using [`@SerialName` annotation](basic-serialization.md#serial-field-names). However, there are certain situations where transformation should be applied to every serial name — such as migration from other frameworks or legacy codebase. For that cases, it is possible to specify a [namingStrategy][JsonBuilder.namingStrategy] for a [Json] instance. `kotlinx.serialization` provides one strategy implementation out of the box, the [JsonNamingStrategy.SnakeCase](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/-builtins/-snake-case.html): ```kotlin @Serializable data class Project(val projectName: String, val projectOwner: String) @OptIn(ExperimentalSerializationApi::class) // namingStrategy is an experimental setting for now val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase } fun main() { val project = format.decodeFromString("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""") println(format.encodeToString(project.copy(projectName = "kotlinx.serialization"))) } ``` > You can get the full code [here](../guide/example/example-json-16.kt). As you can see, both serialization and deserialization work as if all serial names are transformed from camel case to snake case: ```text {"project_name":"kotlinx.serialization","project_owner":"Kotlin"} ``` There are some caveats one should remember while dealing with a [JsonNamingStrategy]: * Due to the nature of the `kotlinx.serialization` framework, naming strategy transformation is applied to all properties regardless of whether their serial name was taken from the property name or provided by [SerialName] annotation. Effectively, it means one cannot avoid transformation by explicitly specifying the serial name. To be able to deserialize non-transformed names, [JsonNames] annotation can be used instead. * Collision of the transformed name with any other (transformed) properties serial names or any alternative names specified with [JsonNames] will lead to a deserialization exception. * Global naming strategies are very implicit: by looking only at the definition of the class, it is impossible to determine which names it will have in the serialized form. As a consequence, naming strategies are not friendly to actions like Find Usages/Rename in IDE, full-text search by grep, etc. For them, the original name and the transformed are two different things; changing one without the other may introduce bugs in many unexpected ways and lead to greater maintenance efforts for code with global naming strategies. Therefore, one should carefully weigh the pros and cons before considering adding global naming strategies to an application. ### Base64 To encode and decode Base64 formats, we will need to manually write a serializer. Here, we will use a default implementation of Kotlin's Base64 encoder. Note that some serializers use different RFCs for Base64 encoding by default. For example, Jackson uses a variant of [Base64 Mime](https://datatracker.ietf.org/doc/html/rfc2045). The same result in kotlinx.serialization can be achieved with Base64.Mime encoder. [Kotlin's documentation for Base64](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io.encoding/-base64/) lists other available encoders. ```kotlin import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.descriptors.* import kotlin.io.encoding.* @OptIn(ExperimentalEncodingApi::class) object ByteArrayAsBase64Serializer : KSerializer { private val base64 = Base64.Default override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor( "ByteArrayAsBase64Serializer", PrimitiveKind.STRING ) override fun serialize(encoder: Encoder, value: ByteArray) { val base64Encoded = base64.encode(value) encoder.encodeString(base64Encoded) } override fun deserialize(decoder: Decoder): ByteArray { val base64Decoded = decoder.decodeString() return base64.decode(base64Decoded) } } ``` For more details on how to create your own custom serializer, you can see [custom serializers](serializers.md#custom-serializers). Then we can use it like this: ```kotlin @Serializable data class Value( @Serializable(with = ByteArrayAsBase64Serializer::class) val base64Input: ByteArray ) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as Value return base64Input.contentEquals(other.base64Input) } override fun hashCode(): Int { return base64Input.contentHashCode() } } fun main() { val string = "foo string" val value = Value(string.toByteArray()) val encoded = Json.encodeToString(value) println(encoded) val decoded = Json.decodeFromString(encoded) println(decoded.base64Input.decodeToString()) } ``` > You can get the full code [here](../guide/example/example-json-17.kt) ```text {"base64Input":"Zm9vIHN0cmluZw=="} foo string ``` Notice the serializer we wrote is not dependent on `Json` format, therefore, it can be used in any format. For projects that use this serializer in many places, to avoid specifying the serializer every time, it is possible to [specify a serializer globally using typealias](serializers.md#specifying-serializer-globally-using-typealias). For example: ````kotlin typealias Base64ByteArray = @Serializable(ByteArrayAsBase64Serializer::class) ByteArray ```` ## Json elements Aside from direct conversions between strings and JSON objects, Kotlin serialization offers APIs that allow other ways of working with JSON in the code. For example, you might need to tweak the data before it can parse or otherwise work with such an unstructured data that it does not readily fit into the typesafe world of Kotlin serialization. The main concept in this part of the library is [JsonElement]. Read on to learn what you can do with it. ### Parsing to Json element A string can be _parsed_ into an instance of [JsonElement] with the [Json.parseToJsonElement] function. It is called neither decoding nor deserialization because none of that happens in the process. It just parses a JSON and forms an object representing it: ```kotlin fun main() { val element = Json.parseToJsonElement(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) println(element) } ``` > You can get the full code [here](../guide/example/example-json-18.kt). A `JsonElement` prints itself as a valid JSON: ```text {"name":"kotlinx.serialization","language":"Kotlin"} ``` ### Types of Json elements A [JsonElement] class has three direct subtypes, closely following JSON grammar: * [JsonPrimitive] represents primitive JSON elements, such as string, number, boolean, and null. Each primitive has a simple string [content][JsonPrimitive.content]. There is also a [JsonPrimitive()] constructor function overloaded to accept various primitive Kotlin types and to convert them to `JsonPrimitive`. * [JsonArray] represents a JSON `[...]` array. It is a Kotlin [List] of `JsonElement` items. * [JsonObject] represents a JSON `{...}` object. It is a Kotlin [Map] from `String` keys to `JsonElement` values. The `JsonElement` class has extensions that cast it to its corresponding subtypes: [jsonPrimitive][_jsonPrimitive], [jsonArray][_jsonArray], [jsonObject][_jsonObject]. The `JsonPrimitive` class, in turn, provides converters to Kotlin primitive types: [int], [intOrNull], [long], [longOrNull], and similar ones for other types. This is how you can use them for processing JSON whose structure you know: ```kotlin fun main() { val element = Json.parseToJsonElement(""" { "name": "kotlinx.serialization", "forks": [{"votes": 42}, {"votes": 9000}, {}] } """) val sum = element .jsonObject["forks"]!! .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 } println(sum) } ``` > You can get the full code [here](../guide/example/example-json-19.kt). The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`: ```text 9042 ``` Note that the execution will fail if the structure of the data is otherwise different. ### Json element builders You can construct instances of specific [JsonElement] subtypes using the respective builder functions [buildJsonArray] and [buildJsonObject]. They provide a DSL to define the resulting JSON structure. It is similar to Kotlin standard library collection builders, but with a JSON-specific convenience of more type-specific overloads and inner builder functions. The following example shows all the key features: ```kotlin fun main() { val element = buildJsonObject { put("name", "kotlinx.serialization") putJsonObject("owner") { put("name", "kotlin") } putJsonArray("forks") { addJsonObject { put("votes", 42) } addJsonObject { put("votes", 9000) } } } println(element) } ``` > You can get the full code [here](../guide/example/example-json-20.kt). As a result, you get a proper JSON string: ```text {"name":"kotlinx.serialization","owner":{"name":"kotlin"},"forks":[{"votes":42},{"votes":9000}]} ``` ### Decoding Json elements An instance of the [JsonElement] class can be decoded into a serializable object using the [Json.decodeFromJsonElement] function: ```kotlin @Serializable data class Project(val name: String, val language: String) fun main() { val element = buildJsonObject { put("name", "kotlinx.serialization") put("language", "Kotlin") } val data = Json.decodeFromJsonElement(element) println(data) } ``` > You can get the full code [here](../guide/example/example-json-21.kt). The result is exactly what you would expect: ```text Project(name=kotlinx.serialization, language=Kotlin) ``` ### Encoding literal Json content In some cases it might be necessary to encode an arbitrary unquoted value. This can be achieved with [JsonUnquotedLiteral]. #### Serializing large decimal numbers The JSON specification does not restrict the size or precision of numbers, however it is not possible to serialize numbers of arbitrary size or precision using [JsonPrimitive()]. If [Double] is used, then the numbers are limited in precision, meaning that large numbers are truncated. When using Kotlin/JVM [BigDecimal] can be used instead, but [JsonPrimitive()] will encode the value as a string, not a number. ```kotlin import java.math.BigDecimal val format = Json { prettyPrint = true } fun main() { val pi = BigDecimal("3.141592653589793238462643383279") val piJsonDouble = JsonPrimitive(pi.toDouble()) val piJsonString = JsonPrimitive(pi.toString()) val piObject = buildJsonObject { put("pi_double", piJsonDouble) put("pi_string", piJsonString) } println(format.encodeToString(piObject)) } ``` > You can get the full code [here](../guide/example/example-json-22.kt). Even though `pi` was defined as a number with 30 decimal places, the resulting JSON does not reflect this. The [Double] value is truncated to 15 decimal places, and the String is wrapped in quotes - which is not a JSON number. ```text { "pi_double": 3.141592653589793, "pi_string": "3.141592653589793238462643383279" } ``` To avoid precision loss, the string value of `pi` can be encoded using [JsonUnquotedLiteral]. ```kotlin import java.math.BigDecimal val format = Json { prettyPrint = true } fun main() { val pi = BigDecimal("3.141592653589793238462643383279") // use JsonUnquotedLiteral to encode raw JSON content val piJsonLiteral = JsonUnquotedLiteral(pi.toString()) val piJsonDouble = JsonPrimitive(pi.toDouble()) val piJsonString = JsonPrimitive(pi.toString()) val piObject = buildJsonObject { put("pi_literal", piJsonLiteral) put("pi_double", piJsonDouble) put("pi_string", piJsonString) } println(format.encodeToString(piObject)) } ``` > You can get the full code [here](../guide/example/example-json-23.kt). `pi_literal` now accurately matches the value defined. ```text { "pi_literal": 3.141592653589793238462643383279, "pi_double": 3.141592653589793, "pi_string": "3.141592653589793238462643383279" } ``` To decode `pi` back to a [BigDecimal], the string content of the [JsonPrimitive] can be used. (This demonstration uses a [JsonPrimitive] for simplicity. For a more re-usable method of handling serialization, see [Json Transformations](#json-transformations) below.) ```kotlin import java.math.BigDecimal fun main() { val piObjectJson = """ { "pi_literal": 3.141592653589793238462643383279 } """.trimIndent() val piObject: JsonObject = Json.decodeFromString(piObjectJson) val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content val pi = BigDecimal(piJsonLiteral) println(pi) } ``` > You can get the full code [here](../guide/example/example-json-24.kt). The exact value of `pi` is decoded, with all 30 decimal places of precision that were in the source JSON. ```text 3.141592653589793238462643383279 ``` #### Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden To avoid creating an inconsistent state, encoding a String equal to `"null"` is forbidden. Use [JsonNull] or [JsonPrimitive] instead. ```kotlin fun main() { // caution: creating null with JsonUnquotedLiteral will cause an exception! JsonUnquotedLiteral("null") } ``` > You can get the full code [here](../guide/example/example-json-25.kt). ```text Exception in thread "main" kotlinx.serialization.json.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. ``` ## Json transformations To affect the shape and contents of JSON output after serialization, or adapt input to deserialization, it is possible to write a [custom serializer](serializers.md). However, it may be inconvenient to carefully follow [Encoder] and [Decoder] calling conventions, especially for relatively small and easy tasks. For that purpose, Kotlin serialization provides an API that can reduce the burden of implementing a custom serializer to a problem of manipulating a Json elements tree. We recommend that you get familiar with the [Serializers](serializers.md) chapter: among other things, it explains how custom serializers are bound to classes. Transformation capabilities are provided by the abstract [JsonTransformingSerializer] class which implements [KSerializer]. Instead of direct interaction with `Encoder` or `Decoder`, this class asks you to supply transformations for JSON tree represented by the [JsonElement] class using the`transformSerialize` and `transformDeserialize` methods. Let's take a look at the examples. ### Array wrapping The first example is an implementation of JSON array wrapping for lists. Consider a REST API that returns a JSON array of `User` objects, or a single object (not wrapped into an array) if there is only one element in the result. In the data model, use the [`@Serializable`][Serializable] annotation to specify a custom serializer for a `users: List` property. ```kotlin @Serializable data class Project( val name: String, @Serializable(with = UserListSerializer::class) val users: List ) @Serializable data class User(val name: String) ``` Since this example covers only the deserialization case, you can implement `UserListSerializer` and override only the `transformDeserialize` function. The `JsonTransformingSerializer` constructor takes an original serializer as parameter (this approach is shown in the section [Constructing collection serializers](serializers.md#constructing-collection-serializers)): ```kotlin object UserListSerializer : JsonTransformingSerializer(ListSerializer(User.serializer())) { // If response is not an array, then it is a single object that should be wrapped into the array override fun transformDeserialize(element: JsonElement): JsonElement = if (element !is JsonArray) JsonArray(listOf(element)) else element } ``` Now you can test the code with a JSON array or a single JSON object as inputs. ```kotlin fun main() { println(Json.decodeFromString(""" {"name":"kotlinx.serialization","users":{"name":"kotlin"}} """)) println(Json.decodeFromString(""" {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]} """)) } ``` > You can get the full code [here](../guide/example/example-json-26.kt). The output shows that both cases are correctly deserialized into a Kotlin [List]. ```text Project(name=kotlinx.serialization, users=[User(name=kotlin)]) Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)]) ``` ### Array unwrapping You can also implement the `transformSerialize` function to unwrap a single-element list into a single JSON object during serialization: ```kotlin override fun transformSerialize(element: JsonElement): JsonElement { require(element is JsonArray) // this serializer is used only with lists return element.singleOrNull() ?: element } ``` Now, if you serialize a single-element list of objects from Kotlin: ```kotlin fun main() { val data = Project("kotlinx.serialization", listOf(User("kotlin"))) println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-json-27.kt). You end up with a single JSON object, not an array with one element: ```text {"name":"kotlinx.serialization","users":{"name":"kotlin"}} ``` ### Manipulating default values Another kind of useful transformation is omitting specific values from the output JSON, for example, if it is used as default when missing or for other reasons. Imagine that you cannot specify a default value for the `language` property in the `Project` data model for some reason, but you need it omitted from the JSON when it is equal to `Kotlin` (we can all agree that Kotlin should be default anyway). You can fix it by writing the special `ProjectSerializer` based on the [Plugin-generated serializer](serializers.md#plugin-generated-serializer) for the `Project` class. ```kotlin @Serializable class Project(val name: String, val language: String) object ProjectSerializer : JsonTransformingSerializer(Project.serializer()) { override fun transformSerialize(element: JsonElement): JsonElement = // Filter out top-level key value pair with the key "language" and the value "Kotlin" JsonObject(element.jsonObject.filterNot { (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin" }) } ``` In the example below, we are serializing the `Project` class at the top-level, so we explicitly pass the above `ProjectSerializer` to [Json.encodeToString] function as was shown in the [Passing a serializer manually](serializers.md#passing-a-serializer-manually) section: ```kotlin fun main() { val data = Project("kotlinx.serialization", "Kotlin") println(Json.encodeToString(data)) // using plugin-generated serializer println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer } ``` > You can get the full code [here](../guide/example/example-json-28.kt). See the effect of the custom serializer: ```text {"name":"kotlinx.serialization","language":"Kotlin"} {"name":"kotlinx.serialization"} ``` ### Content-based polymorphic deserialization Typically, [polymorphic serialization](polymorphism.md) requires a dedicated `"type"` key (also known as _class discriminator_) in the incoming JSON object to determine the actual serializer which should be used to deserialize Kotlin class. However, sometimes the `type` property may not be present in the input. In this case, you need to guess the actual type by the shape of JSON, for example by the presence of a specific key. [JsonContentPolymorphicSerializer] provides a skeleton implementation for such a strategy. To use it, override its `selectDeserializer` method. Let's start with the following class hierarchy. > Note that is does not have to be `sealed` as recommended in the [Sealed classes](polymorphism.md#sealed-classes) section, > because we are not going to take advantage of the plugin-generated code that automatically selects the > appropriate subclass, but are going to implement this code manually. ```kotlin @Serializable abstract class Project { abstract val name: String } @Serializable data class BasicProject(override val name: String): Project() @Serializable data class OwnedProject(override val name: String, val owner: String) : Project() ``` You can distinguish the `BasicProject` and `OwnedProject` subclasses by the presence of the `owner` key in the JSON object. ```kotlin object ProjectSerializer : JsonContentPolymorphicSerializer(Project::class) { override fun selectDeserializer(element: JsonElement) = when { "owner" in element.jsonObject -> OwnedProject.serializer() else -> BasicProject.serializer() } } ``` When you use this serializer to serialize data, either [registered](polymorphism.md#registered-subclasses) or the default serializer is selected for the actual type at runtime: ```kotlin fun main() { val data = listOf( OwnedProject("kotlinx.serialization", "kotlin"), BasicProject("example") ) val string = Json.encodeToString(ListSerializer(ProjectSerializer), data) println(string) println(Json.decodeFromString(ListSerializer(ProjectSerializer), string)) } ``` > You can get the full code [here](../guide/example/example-json-29.kt). No class discriminator is added in the JSON output: ```text [{"name":"kotlinx.serialization","owner":"kotlin"},{"name":"example"}] [OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)] ``` ### Extending the behavior of the plugin generated serializer In some cases, it may be necessary to add additional serialization logic on top of the plugin generated logic. For example, to add a preliminary modification of JSON elements or to add processing of unknown values of enums. In this case, you can mark the serializable class with the [`@KeepGeneratedSerializer`][KeepGeneratedSerializer] annotation and get the generated serializer using the `generatedSerializer()` function. > This annotation is currently experimental. Kotlin 2.0.20 or higher is required for this feature to work. Here is an example of the simultaneous use of [JsonTransformingSerializer] and polymorphism. In this example, we use `transformDeserialize` function to rename `basic-name` key into `name` so it matches the `abstract val name` property from the `Project` supertype. ```kotlin @Serializable sealed class Project { abstract val name: String } @OptIn(ExperimentalSerializationApi::class) @KeepGeneratedSerializer @Serializable(with = BasicProjectSerializer::class) @SerialName("basic") data class BasicProject(override val name: String): Project() object BasicProjectSerializer : JsonTransformingSerializer(BasicProject.generatedSerializer()) { override fun transformDeserialize(element: JsonElement): JsonElement { val jsonObject = element.jsonObject return if ("basic-name" in jsonObject) { val nameElement = jsonObject["basic-name"] ?: throw IllegalStateException() JsonObject(mapOf("name" to nameElement)) } else { jsonObject } } } fun main() { val project = Json.decodeFromString("""{"type":"basic","basic-name":"example"}""") println(project) } ``` > You can get the full code [here](../guide/example/example-json-30.kt). `BasicProject` will be printed to the output: ```text BasicProject(name=example) ``` ### Under the hood (experimental) Although abstract serializers mentioned above can cover most of the cases, it is possible to implement similar machinery manually, using only the [KSerializer] class. If tweaking the abstract methods `transformSerialize`/`transformDeserialize`/`selectDeserializer` is not enough, then altering `serialize`/`deserialize` is a way to go. Here are some useful things about custom serializers with [Json]: * [Encoder] can be cast to [JsonEncoder], and [Decoder] to [JsonDecoder], if the current format is [Json]. * `JsonDecoder` has the [decodeJsonElement][JsonDecoder.decodeJsonElement] method and `JsonEncoder` has the [encodeJsonElement][JsonEncoder.encodeJsonElement] method, which basically retrieve an element from and insert an element to a current position in the stream. * Both [`JsonDecoder`][JsonDecoder.json] and [`JsonEncoder`][JsonEncoder.json] have the `json` property, which returns [Json] instance with all settings that are currently in use. * [Json] has the [encodeToJsonElement][Json.encodeToJsonElement] and [decodeFromJsonElement][Json.decodeFromJsonElement] methods. Given all that, it is possible to implement two-stage conversion `Decoder -> JsonElement -> value` or `value -> JsonElement -> Encoder`. For example, you can implement a fully custom serializer for the following `Response` class so that its `Ok` subclass is represented directly, but the `Error` subclass is represented by an object with the error message: ```kotlin @Serializable(with = ResponseSerializer::class) sealed class Response { data class Ok(val data: T) : Response() data class Error(val message: String) : Response() } class ResponseSerializer(private val dataSerializer: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Response") { element("Ok", dataSerializer.descriptor) element("Error", buildClassSerialDescriptor("Error") { element("message") }) } override fun deserialize(decoder: Decoder): Response { // Decoder -> JsonDecoder require(decoder is JsonDecoder) // this class can be decoded only by Json // JsonDecoder -> JsonElement val element = decoder.decodeJsonElement() // JsonElement -> value if (element is JsonObject && "error" in element) return Response.Error(element["error"]!!.jsonPrimitive.content) return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element)) } override fun serialize(encoder: Encoder, value: Response) { // Encoder -> JsonEncoder require(encoder is JsonEncoder) // This class can be encoded only by Json // value -> JsonElement val element = when (value) { is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data) is Response.Error -> buildJsonObject { put("error", value.message) } } // JsonElement -> JsonEncoder encoder.encodeJsonElement(element) } } ``` Having this serializable `Response` implementation, you can take any serializable payload for its data and serialize or deserialize the corresponding responses: ```kotlin @Serializable data class Project(val name: String) fun main() { val responses = listOf( Response.Ok(Project("kotlinx.serialization")), Response.Error("Not found") ) val string = Json.encodeToString(responses) println(string) println(Json.decodeFromString>(string)) } ``` > You can get the full code [here](../guide/example/example-json-31.kt). This gives you fine-grained control on the representation of the `Response` class in the JSON output: ```text [{"name":"kotlinx.serialization"},{"error":"Not found"}] [Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)] ``` ### Maintaining custom JSON attributes A good example of custom JSON-specific serializer would be a deserializer that packs all unknown JSON properties into a dedicated field of `JsonObject` type. Let's add `UnknownProject` – a class with the `name` property and arbitrary details flattened into the same object: ```kotlin data class UnknownProject(val name: String, val details: JsonObject) ``` However, the default plugin-generated serializer requires details to be a separate JSON object and that's not what we want. To mitigate that, write an own serializer that uses the fact that it works only with the `Json` format: ```kotlin object UnknownProjectSerializer : KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") { element("name") element("details") } override fun deserialize(decoder: Decoder): UnknownProject { // Cast to JSON-specific interface val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON") // Read the whole content as JSON val json = jsonInput.decodeJsonElement().jsonObject // Extract and remove name property val name = json.getValue("name").jsonPrimitive.content val details = json.toMutableMap() details.remove("name") return UnknownProject(name, JsonObject(details)) } override fun serialize(encoder: Encoder, value: UnknownProject) { error("Serialization is not supported") } } ``` Now it can be used to read flattened JSON details as `UnknownProject`: ```kotlin fun main() { println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}""")) } ``` > You can get the full code [here](../guide/example/example-json-32.kt). ```text UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"}) ``` --- The next chapter covers [Alternative and custom formats (experimental)](formats.md). [RFC-4627]: https://www.ietf.org/rfc/rfc4627.txt [BigDecimal]: https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html [Double]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/ [Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html [List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/ [Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/ [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html [InheritableSerialInfo]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/index.html [KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html [KeepGeneratedSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-keep-generated-serializer/index.html [Encoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html [Decoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html [Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html [Json()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html [JsonBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/index.html [JsonBuilder.prettyPrint]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html [JsonBuilder.isLenient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html [JsonBuilder.ignoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html [JsonIgnoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-ignore-unknown-keys/index.html [JsonNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/index.html [JsonBuilder.useAlternativeNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html [JsonBuilder.encodeDefaults]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html [JsonBuilder.explicitNulls]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html [JsonBuilder.coerceInputValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html [JsonBuilder.allowStructuredMapKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html [JsonBuilder.allowSpecialFloatingPointValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html [JsonBuilder.classDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html [JsonClassDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/index.html [JsonBuilder.classDiscriminatorMode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator-mode.html [ClassDiscriminatorMode.NONE]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-n-o-n-e/index.html [ClassDiscriminatorMode.POLYMORPHIC]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-p-o-l-y-m-o-r-p-h-i-c/index.html [ClassDiscriminatorMode.ALL_JSON_OBJECTS]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-a-l-l_-j-s-o-n_-o-b-j-e-c-t-s/index.html [JsonBuilder.decodeEnumsCaseInsensitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/decode-enums-case-insensitive.html [JsonBuilder.namingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/naming-strategy.html [JsonNamingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/index.html [JsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/index.html [Json.parseToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html [JsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/index.html [JsonPrimitive.content]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/content.html [JsonPrimitive()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive.html [JsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-array/index.html [JsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-object/index.html [_jsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-primitive.html [_jsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-array.html [_jsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-object.html [int]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int.html [intOrNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int-or-null.html [long]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long.html [longOrNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long-or-null.html [buildJsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-array.html [buildJsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-object.html [Json.decodeFromJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html [JsonUnquotedLiteral]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-unquoted-literal.html [JsonNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-null/index.html [JsonTransformingSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-transforming-serializer/index.html [Json.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html [JsonContentPolymorphicSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-content-polymorphic-serializer/index.html [JsonEncoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/index.html [JsonDecoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/index.html [JsonDecoder.decodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/decode-json-element.html [JsonEncoder.encodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/encode-json-element.html [JsonDecoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/json.html [JsonEncoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/json.html [Json.encodeToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html --- ## File: docs/migration.md # Migration from 0.20.0 version to 1.0.0 For adopters of earlier versions of `kotlinx.serialization`, a dedicated migration path is prepared. During the preparation of serialization 1.0.0 release, most of the API has been changed, renamed, moved to a separate package or made internal. IDEA migrations were introduced, but unfortunately not all API can be migrated with automatic replacements. To simplify your migrations path, it is recommended to enable star imports in IDE (so all extensions are imported automatically) first. 1. Update `kotlinx.serialization` to version `1.0.0-RC2` (this is the last version that has migrations for pre-1.0.0 versions. 1.0.0 version itself does not have any migration aids.) 2. Rename dependency from `kotlinx-serialization-runtime` to `kotlinx-serialization-json`. 3. For multiplatform usages, remove dependencies to platform-specific artifacts (e.g. `kotlinx-serialization-runtime-js`), they are [no longer required](/README.md#multiplatform-common-js-native) by Gradle. 4. Update Kotlin to 1.4.0 or higher. 5. Start applying replacements for the deprecated code. 6. If some signatures are not resolved, try to hit `alt + Enter` and import the signature. 7. If methods are still not resolved, it is recommended to use star imports for `kotlinx.serialization` signatures in the problematic file. 8. When there are no usages of deprecated code left, you can change dependency version from `1.0.0-RC2` to `1.0.0`. For less trivial issues, it is recommended to study [the changelog](../CHANGELOG.md#100-rc--2020-08-17) or to ask for help in `#serialization` Kotlin's Slack channel. --- ## File: docs/polymorphism.md # Polymorphism This is the fourth chapter of the [Kotlin Serialization Guide](serialization-guide.md). In this chapter we'll see how Kotlin Serialization deals with polymorphic class hierarchies. **Table of contents** * [Closed polymorphism](#closed-polymorphism) * [Static types](#static-types) * [Designing serializable hierarchy](#designing-serializable-hierarchy) * [Sealed classes](#sealed-classes) * [Custom subclass serial name](#custom-subclass-serial-name) * [Concrete properties in a base class](#concrete-properties-in-a-base-class) * [Objects](#objects) * [Open polymorphism](#open-polymorphism) * [Registered subclasses](#registered-subclasses) * [Serializing interfaces](#serializing-interfaces) * [Registering sealed children as subclasses](#registering-sealed-children-as-subclasses) * [Property of an interface type](#property-of-an-interface-type) * [Static parent type lookup for polymorphism](#static-parent-type-lookup-for-polymorphism) * [Explicitly marking polymorphic class properties](#explicitly-marking-polymorphic-class-properties) * [Registering multiple superclasses](#registering-multiple-superclasses) * [Polymorphism and generic classes](#polymorphism-and-generic-classes) * [Merging library serializers modules](#merging-library-serializers-modules) * [Default polymorphic type handler for deserialization](#default-polymorphic-type-handler-for-deserialization) * [Default polymorphic type handler for serialization](#default-polymorphic-type-handler-for-serialization) ## Closed polymorphism Let us start with basic introduction to polymorphism. ### Static types Kotlin Serialization is fully static with respect to types by default. The structure of encoded objects is determined by *compile-time* types of objects. Let's examine this aspect in more detail and learn how to serialize polymorphic data structures, where the type of data is determined at runtime. To show the static nature of Kotlin Serialization let us make the following setup. An `open class Project` has just the `name` property, while its derived `class OwnedProject` adds an `owner` property. In the below example, we serialize `data` variable with a static type of `Project` that is initialized with an instance of `OwnedProject` at runtime. ```kotlin @Serializable open class Project(val name: String) class OwnedProject(name: String, val owner: String) : Project(name) fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-01.kt). Despite the runtime type of `OwnedProject`, only the `Project` class properties are getting serialized. ```text {"name":"kotlinx.coroutines"} ``` Let's change the compile-time type of `data` to `OwnedProject`. ```kotlin @Serializable open class Project(val name: String) class OwnedProject(name: String, val owner: String) : Project(name) fun main() { val data = OwnedProject("kotlinx.coroutines", "kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-02.kt). We get an error, because the `OwnedProject` class is not serializable. ```text Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'OwnedProject' is not found. Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied. ``` ### Designing serializable hierarchy We cannot simply mark `OwnedProject` from the previous example as `@Serializable`. It does not compile, running into the [constructor properties requirement](basic-serialization.md#constructor-properties-requirement). To make hierarchy of classes serializable, the properties in the parent class have to be marked `abstract`, making the `Project` class `abstract`, too. ```kotlin @Serializable abstract class Project { abstract val name: String } class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-03.kt). This is close to the best design for a serializable hierarchy of classes, but running it produces the following error: ```text Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for subclass 'OwnedProject' is not found in the polymorphic scope of 'Project'. Check if class with serial name 'OwnedProject' exists and serializer is registered in a corresponding SerializersModule. To be registered automatically, class 'OwnedProject' has to be '@Serializable', and the base class 'Project' has to be sealed and '@Serializable'. ``` ### Sealed classes The most straightforward way to use serialization with a polymorphic hierarchy is to mark the base class `sealed`. _All_ subclasses of a sealed class must be explicitly marked as `@Serializable`. ```kotlin @Serializable sealed class Project { abstract val name: String } @Serializable class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(Json.encodeToString(data)) // Serializing data of compile-time type Project } ``` > You can get the full code [here](../guide/example/example-poly-04.kt). Now we can see a default way to represent polymorphism in JSON. A `type` key is added to the resulting JSON object as a _discriminator_. ```text {"type":"example.examplePoly04.OwnedProject","name":"kotlinx.coroutines","owner":"kotlin"} ``` Pay attention to the small, but very important detail in the above example that is related to [Static types](#static-types): the `val data` property has a compile-time type of `Project`, even though its run-time type is `OwnedProject`. When serializing polymorphic class hierarchies you must ensure that the compile-time type of the serialized object is a polymorphic one, not a concrete one. Let us see what happens if the example is slightly changed, so that the compile-time of the object that is being serialized is `OwnedProject` (the same as its run-time type). ```kotlin @Serializable sealed class Project { abstract val name: String } @Serializable class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data = OwnedProject("kotlinx.coroutines", "kotlin") // data: OwnedProject here println(Json.encodeToString(data)) // Serializing data of compile-time type OwnedProject } ``` > You can get the full code [here](../guide/example/example-poly-05.kt). The type of `OwnedProject` is concrete and is not polymorphic, thus the `type` discriminator property is not emitted into the resulting JSON. ```text {"name":"kotlinx.coroutines","owner":"kotlin"} ``` In general, Kotlin Serialization is designed to work correctly only when the compile-time type used during serialization is the same one as the compile-time type used during deserialization. You can always specify the type explicitly when calling serialization functions. The previous example can be corrected to use `Project` type for serialization by calling `Json.encodeToString(data)`. ### Custom subclass serial name A value of the `type` key is a fully qualified class name by default. We can put [SerialName] annotation onto the corresponding class to change it. ```kotlin @Serializable sealed class Project { abstract val name: String } @Serializable @SerialName("owned") class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(Json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-06.kt). This way we can have a stable _serial name_ that is not affected by the class's name in the source code. ```text {"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"} ``` > In addition to that, JSON can be configured to use a different key name for the class discriminator. > You can find an example in the [Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism) section. > [!IMPORTANT] > When picking a serial name for a class, avoid assigning the same name to different classes. Check out equality rules in documentation for [SerialDescriptor] to make sure that the class descriptor will stay unique. ### Concrete properties in a base class A base class in a sealed hierarchy can have properties with backing fields. ```kotlin @Serializable sealed class Project { abstract val name: String var status = "open" } @Serializable @SerialName("owned") class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val json = Json { encodeDefaults = true } // "status" will be skipped otherwise val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(json.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-07.kt). The properties of the superclass are serialized before the properties of the subclass. ```text {"type":"owned","status":"open","name":"kotlinx.coroutines","owner":"kotlin"} ``` ### Objects Sealed hierarchies can have objects as their subclasses and they also need to be marked as `@Serializable`. Let's take a different example with a hierarchy of `Response` classes. ```kotlin @Serializable sealed class Response @Serializable object EmptyResponse : Response() @Serializable class TextResponse(val text: String) : Response() ``` Let us serialize a list of different responses. ```kotlin fun main() { val list = listOf(EmptyResponse, TextResponse("OK")) println(Json.encodeToString(list)) } ``` > You can get the full code [here](../guide/example/example-poly-08.kt). An object serializes as an empty class, also using its fully qualified class name as type by default: ```text [{"type":"example.examplePoly08.EmptyResponse"},{"type":"example.examplePoly08.TextResponse","text":"OK"}] ``` > Even if object has properties, they are not serialized. ## Open polymorphism Serialization can work with arbitrary `open` classes or `abstract` classes. However, since this kind of polymorphism is open, there is a possibility that subclasses are defined anywhere in the source code, even in other modules, the list of subclasses that are serialized cannot be determined at compile-time and must be explicitly registered at runtime. ### Registered subclasses Let us start with the code from the [Designing serializable hierarchy](#designing-serializable-hierarchy) section. To make it work with serialization without making it `sealed`, we have to define a [SerializersModule] using the [SerializersModule {}][SerializersModule()] builder function. In the module the base class is specified in the [polymorphic][_polymorphic] builder and each subclass is registered with the [subclass] function. Now, a custom JSON configuration can be instantiated with this module and used for serialization. > Details on custom JSON configurations can be found in > the [JSON configuration](json.md#json-configuration) section. ```kotlin val module = SerializersModule { polymorphic(Project::class) { subclass(OwnedProject::class) } } val format = Json { serializersModule = module } @Serializable abstract class Project { abstract val name: String } @Serializable @SerialName("owned") class OwnedProject(override val name: String, val owner: String) : Project() fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-09.kt). This additional configuration makes our code work just as it worked with a sealed class in the [Sealed classes](#sealed-classes) section, but here subclasses can be spread arbitrarily throughout the code. ```text {"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"} ``` >Please note that this example works only on JVM because of `serializer` function restrictions. >For JS and Native, explicit serializer should be used: `format.encodeToString(PolymorphicSerializer(Project::class), data)` >You can keep track of this issue [here](https://github.com/Kotlin/kotlinx.serialization/issues/1077). ### Serializing interfaces We can update the previous example and turn `Project` superclass into an interface. However, we cannot mark an interface itself as `@Serializable`. No problem. Interfaces cannot have instances by themselves. Interfaces can only be represented by instances of their derived classes. Interfaces are used in the Kotlin language to enable polymorphism, so all interfaces are considered to be implicitly serializable with the [PolymorphicSerializer] strategy. We just need to mark their implementing classes as `@Serializable` and register them. ```kotlin interface Project { val name: String } @Serializable @SerialName("owned") class OwnedProject(override val name: String, val owner: String) : Project ``` Now if we declare `data` with the type of `Project` we can simply call `format.encodeToString` as before. ```kotlin fun main() { val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-10.kt). ```text {"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"} ``` > Note: On Kotlin/Native, you should use `format.encodeToString(PolymorphicSerializer(Project::class), data))` instead due to limited reflection capabilities. ### Registering sealed children as subclasses A sealed parent interface or class can be used to directly register all its children using `subclassesOfSealed`. This will allow serializing the children using open polymorphism without the need to register each one individually. If one of the type's subclasses is a sealed serializable class on its own, its subclasses are registered recursively as well. However, if one of the type's subclasses is an open polymorphic class, an `IllegalArgumentException` is thrown. In other words, all children/descendants must be either concrete or sealed. ```kotlin interface Base @Serializable sealed interface Sub: Base @Serializable class Sub1(val data: String): Sub val module1 = SerializersModule { polymorphic(Base::class) { subclassesOfSealed(Sub.serializer()) } } val format1 = Json { serializersModule = module1 } ``` Alternatively the convenience overload allows specifying the sealed type as type parameter. ```kotlin val module2 = SerializersModule { polymorphic(Base::class) { subclassesOfSealed() } } val format2 = Json { serializersModule = module2 } ``` Now if we declare `data` with the type of `Base` we can simply call `format.encodeToString` as before. ```kotlin fun main() { val data: Base = Sub1("kotlin") println(format1.encodeToString(data)) println(format2.encodeToString(data)) } ``` ```text {"type":"example.examplePoly11.Sub1","data":"kotlin"} {"type":"example.examplePoly11.Sub1","data":"kotlin"} ``` > You can get the full code [here](../guide/example/example-poly-11.kt). ### Property of an interface type Continuing the previous example, let us see what happens if we use `Project` interface as a property in some other serializable class. Interfaces are implicitly polymorphic, so we can just declare a property of an interface type. ```kotlin @Serializable class Data(val project: Project) // Project is an interface fun main() { val data = Data(OwnedProject("kotlinx.coroutines", "kotlin")) println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-12.kt). As long as we've registered the actual subtype of the interface that is being serialized in the [SerializersModule] of our `format`, we get it working at runtime. ```text {"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}} ``` ### Static parent type lookup for polymorphism During serialization of a polymorphic class the root type of the polymorphic hierarchy (`Project` in our example) is determined statically. Let us take the example with the serializable `abstract class Project`, but change the `main` function to declare `data` as having a type of `Any`: ```kotlin fun main() { val data: Any = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-13.kt). We get the exception. ```text Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found. Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied. ``` We have to register classes for polymorphic serialization with respect for the corresponding static type we use in the source code. First of all, we change our module to register a subclass of `Any`: ```kotlin val module = SerializersModule { polymorphic(Any::class) { subclass(OwnedProject::class) } } ``` Then we can try to serialize the variable of type `Any`: ```kotlin fun main() { val data: Any = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-14.kt). However, `Any` is a class and it is not serializable: ```text Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found. Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied. ``` We must to explicitly pass an instance of [PolymorphicSerializer] for the base class `Any` as the first parameter to the [encodeToString][Json.encodeToString] function. ```kotlin fun main() { val data: Any = OwnedProject("kotlinx.coroutines", "kotlin") println(format.encodeToString(PolymorphicSerializer(Any::class), data)) } ``` > You can get the full code [here](../guide/example/example-poly-15.kt). With the explicit serializer it works as before. ```text {"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"} ``` ### Explicitly marking polymorphic class properties The property of an interface type is implicitly considered polymorphic, since interfaces are all about runtime polymorphism. However, Kotlin Serialization does not compile a serializable class with a property of a non-serializable class type. If we have a property of `Any` class or other non-serializable class, then we must explicitly provide its serialization strategy via the [`@Serializable`][Serializable] annotation as we saw in the [Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property) section. To specify a polymorphic serialization strategy of a property, the special-purpose [`@Polymorphic`][Polymorphic] annotation is used. ```kotlin @Serializable class Data( @Polymorphic // the code does not compile without it val project: Any ) fun main() { val data = Data(OwnedProject("kotlinx.coroutines", "kotlin")) println(format.encodeToString(data)) } ``` > You can get the full code [here](../guide/example/example-poly-16.kt). ### Registering multiple superclasses When the same class gets serialized as a value of properties with different compile-time type from the list of its superclasses, we must register it in the [SerializersModule] for each of its superclasses separately. It is convenient to extract registration of all the subclasses into a separate function and use it for each superclass. You can use the following template to write it. ```kotlin val module = SerializersModule { fun PolymorphicModuleBuilder.registerProjectSubclasses() { subclass(OwnedProject::class) } polymorphic(Any::class) { registerProjectSubclasses() } polymorphic(Project::class) { registerProjectSubclasses() } } ``` > You can get the full code [here](../guide/example/example-poly-17.kt). ### Polymorphism and generic classes Generic subtypes for a serializable class require a special handling. Consider the following hierarchy. ```kotlin @Serializable abstract class Response @Serializable @SerialName("OkResponse") data class OkResponse(val data: T) : Response() ``` Kotlin Serialization does not have a builtin strategy to represent the actually provided argument type for the type parameter `T` when serializing a property of the polymorphic type `OkResponse`. We have to provide this strategy explicitly when defining the serializers module for `Response`. In the below example we use `OkResponse.serializer(...)` to retrieve the [Plugin-generated generic serializer](serializers.md#plugin-generated-generic-serializer) of the `OkResponse` class and instantiate it with the [PolymorphicSerializer] instance with `Any` class as its base. This way, we can serialize an instance of `OkResponse` with any `data` property that was polymorphically registered as a subtype of `Any`. ```kotlin val responseModule = SerializersModule { polymorphic(Response::class) { subclass(OkResponse.serializer(PolymorphicSerializer(Any::class))) } } ``` ### Merging library serializers modules When the application grows in size and splits into source code modules, it may become inconvenient to store all class hierarchies in one serializers module. Let us add a library with the `Project` hierarchy to the code from the previous section. ```kotlin val projectModule = SerializersModule { fun PolymorphicModuleBuilder.registerProjectSubclasses() { subclass(OwnedProject::class) } polymorphic(Any::class) { registerProjectSubclasses() } polymorphic(Project::class) { registerProjectSubclasses() } } ``` We can compose those two modules together using the [plus] operator to merge them, so that we can use them both in the same [Json] format instance. > You can also use the [include][SerializersModuleBuilder.include] function > in the [SerializersModule {}][SerializersModule()] DSL. ```kotlin val format = Json { serializersModule = projectModule + responseModule } ```` Now classes from both hierarchies can be serialized together and deserialized together. ```kotlin fun main() { // both Response and Project are abstract and their concrete subtypes are being serialized val data: Response = OkResponse(OwnedProject("kotlinx.serialization", "kotlin")) val string = format.encodeToString(data) println(string) println(format.decodeFromString(string)) } ``` > You can get the full code [here](../guide/example/example-poly-18.kt). The JSON that is being produced is deeply polymorphic. ```text {"type":"OkResponse","data":{"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}} OkResponse(data=OwnedProject(name=kotlinx.serialization, owner=kotlin)) ``` If you're writing a library or shared module with an abstract class and some implementations of it, you can expose your own serializers module for your clients to use so that a client can combine your module with their modules. ### Default polymorphic type handler for deserialization What happens when we deserialize a subclass that was not registered? ```kotlin fun main() { println(format.decodeFromString(""" {"type":"unknown","name":"example"} """)) } ``` > You can get the full code [here](../guide/example/example-poly-19.kt). We get the following exception. ```text Exception in thread "main" kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 0: Serializer for subclass 'unknown' is not found in the polymorphic scope of 'Project' at path: $ Check if class with serial name 'unknown' exists and serializer is registered in a corresponding SerializersModule. ``` When reading a flexible input we might want to provide some default behavior in this case. For example, we can have a `BasicProject` subtype to represent all kinds of unknown `Project` subtypes. ```kotlin @Serializable abstract class Project { abstract val name: String } @Serializable data class BasicProject(override val name: String, val type: String): Project() @Serializable @SerialName("OwnedProject") data class OwnedProject(override val name: String, val owner: String) : Project() ``` We register a default deserializer handler using the [`defaultDeserializer`][PolymorphicModuleBuilder.defaultDeserializer] function in the [`polymorphic { ... }`][PolymorphicModuleBuilder] DSL that defines a strategy which maps the `type` string from the input to the [deserialization strategy][DeserializationStrategy]. In the below example we don't use the type, but always return the [Plugin-generated serializer](serializers.md#plugin-generated-serializer) of the `BasicProject` class. ```kotlin val module = SerializersModule { polymorphic(Project::class) { subclass(OwnedProject::class) defaultDeserializer { BasicProject.serializer() } } } ``` Using this module we can now deserialize both instances of the registered `OwnedProject` and any unregistered one. ```kotlin val format = Json { serializersModule = module } fun main() { println(format.decodeFromString(""" [ {"type":"unknown","name":"example"}, {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"} ] """)) } ``` > You can get the full code [here](../guide/example/example-poly-20.kt). Notice, how `BasicProject` had also captured the specified type key in its `type` property. ```text [BasicProject(name=example, type=unknown), OwnedProject(name=kotlinx.serialization, owner=kotlin)] ``` We used a plugin-generated serializer as a default serializer, implying that the structure of the "unknown" data is known in advance. In a real-world API it's rarely the case. For that purpose a custom, less-structured serializer is needed. You will see the example of such serializer in the future section on [Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes). ### Default polymorphic type handler for serialization Sometimes you need to dynamically choose which serializer to use for a polymorphic type based on the instance, for example if you don't have access to the full type hierarchy, or if it changes a lot. For this situation, you can register a default serializer. ```kotlin interface Animal { } interface Cat : Animal { val catType: String } interface Dog : Animal { val dogType: String } private class CatImpl : Cat { override val catType: String = "Tabby" } private class DogImpl : Dog { override val dogType: String = "Husky" } object AnimalProvider { fun createCat(): Cat = CatImpl() fun createDog(): Dog = DogImpl() } ``` We register a default serializer handler using the [`polymorphicDefaultSerializer`][SerializersModuleBuilder.polymorphicDefaultSerializer] function in the [`SerializersModule { ... }`][SerializersModuleBuilder] DSL that defines a strategy which takes an instance of the base class and provides a [serialization strategy][SerializationStrategy]. In the below example we use a `when` block to check the type of the instance, without ever having to refer to the private implementation classes. ```kotlin val module = SerializersModule { polymorphicDefaultSerializer(Animal::class) { instance -> @Suppress("UNCHECKED_CAST") when (instance) { is Cat -> CatSerializer as SerializationStrategy is Dog -> DogSerializer as SerializationStrategy else -> null } } } object CatSerializer : SerializationStrategy { override val descriptor = buildClassSerialDescriptor("Cat") { element("catType") } override fun serialize(encoder: Encoder, value: Cat) { encoder.encodeStructure(descriptor) { encodeStringElement(descriptor, 0, value.catType) } } } object DogSerializer : SerializationStrategy { override val descriptor = buildClassSerialDescriptor("Dog") { element("dogType") } override fun serialize(encoder: Encoder, value: Dog) { encoder.encodeStructure(descriptor) { encodeStringElement(descriptor, 0, value.dogType) } } } ``` Using this module we can now serialize instances of `Cat` and `Dog`. ```kotlin val format = Json { serializersModule = module } fun main() { println(format.encodeToString(AnimalProvider.createCat())) } ``` > You can get the full code [here](../guide/example/example-poly-21.kt) ```text {"type":"Cat","catType":"Tabby"} ``` --- The next chapter covers [JSON features](json.md). [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html [PolymorphicSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic-serializer/index.html [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html [Polymorphic]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic/index.html [DeserializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/index.html [SerializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/index.html [SerialDescriptor]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html [SerializersModule]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module/index.html [SerializersModule()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module.html [_polymorphic]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/polymorphic.html [subclass]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/subclass.html [plus]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/plus.html [SerializersModuleBuilder.include]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/include.html [PolymorphicModuleBuilder.defaultDeserializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/default-deserializer.html [PolymorphicModuleBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/index.html [SerializersModuleBuilder.polymorphicDefaultSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/polymorphic-default-serializer.html [SerializersModuleBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/index.html [Json.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html [Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html ## 2. Official Technical Reference & Guides (Kotlin/Kotlin.github.io) # Kotlin GitHub Page [](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) This repo exists solely to populate [kotlin.github.io](https://kotlin.github.io) and redirects to [kotlinlang.org](https://kotlinlang.org). It also provides titles and favicons for `kotlin.github.io/*` websites in search consoles like Google. ### Code of Conduct The JetBrains Code of Conduct can be found [here](https://github.com/JetBrains/.github/tree/main/profile#code-of-conduct) and this project also adheres to it. ### Contribution We won't accept pull requests unless there's broken/outdated behavior regarding either: - the favicon (for instance, it's out of date) - the title - the redirect to [kotlinlang.org](https://kotlinlang.org) - a missing html tag that prevents other search consoles from picking up the relevant icon or title(s). ### Why does this exist? This repo was created to fix the issue [KTL-1336](https://youtrack.jetbrains.com/issue/KTL-1336/Add-favicon-and-title-for-kotlin.github.io).