## 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).