Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
378 changes: 378 additions & 0 deletions docs-website/topics/serialization-serialize-builtin-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
[//]: # (title: Serialize built-in types)

The Kotlin serialization library supports various built-in types, including basic types such as primitives and strings, composite types, and several standard library classes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've mentioned 'composite types' without explaining what they are. I suggest just leaving 'basic types such as primitives and strings, as well as certain standard library classes'

The following sections describe these types in detail and provide examples of how to serialize them.

## Basic types

Kotlin serialization provides built-in serializers for types that are represented as a single value in serialized data.
This includes, primitives, strings, and enums.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary , after 'includes' ?


For example, here’s how you can serialize a `Long` type:

```kotlin
// Imports the necessary library declarations
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Data(val signature: Long)

fun main() {
val data = Data(0x1CAFE2FEED0BABE0)
println(Json.encodeToString(data))
// {"signature":2067120338512882656}
}
//sampleEnd
```
{kotlin-runnable="true"}

### Numbers

You can serialize all Kotlin number types, including integers and floating-point numbers, using their natural JSON representations:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.math.PI

//sampleStart
@Serializable
class Data(
val answer: Int,
val pi: Double
)

fun main() {
val data = Data(42, PI)
println(Json.encodeToString(data))
// {"answer":42,"pi":3.141592653589793}
}
//sampleEnd
```
{kotlin-runnable="true"}

### Long numbers as strings

When you serialize Kotlin `Long` values to JSON, JavaScript's native number type can't represent the full range of a Kotlin `Long` type,
leading to precision loss.
Comment on lines +58 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worthwhile to note that this is a limitation of JS, not Json that is effectively arbitrary precision? It is somewhat implied by the explanation below, but can be confusing in the distinction.


Kotlin/JS handles these large `Long` numbers correctly, but JavaScript's native methods don't.
A common workaround is to represent long numbers with full precision using the JSON string type.
Kotlin Serialization supports this approach with [`LongAsStringSerializer`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-long-as-string-serializer/).
To apply it to a `Long` property, use the `@Serializable` annotation:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Data(
@Serializable(with=LongAsStringSerializer::class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer removing explicit with: @Serializable(LongAsStringSerializer::class)

val signature: Long
)

fun main() {
val data = Data(0x1CAFE2FEED0BABE0)
println(Json.encodeToString(data))
// {"signature":"2067120338512882656"}
}
//sampleEnd
```
{kotlin-runnable="true"}

> You can also specify serializers like `LongAsStringSerializer` for all properties in a file.
> For more information, see [Specify serializers for a file](third-party-classes.md#specify-serializers-for-a-file).
>
{style="tip"}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might worth it to add section aboun unsigned numbers here as well, given they're stable in stdlib and IR compiler has been default for years (https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/value-classes.md#unsigned-types-support-json-only). Just don't forget mentioning

Unsigned types are output as unsigned only in JSON format. Other formats such as ProtoBuf and CBOR use built-in serializers that use an underlying signed representation for unsigned types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stating that unsigned is only unsigned in Json is inaccurate, other formats support unsigned directly (and at full range) - maybe something that the protobuf serializer can be extended to do.

### Enum classes

All `enum` classes are serializable by default without the `@Serializable` annotation.
When serialized in JSON, an `enum` is encoded as a string:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
// The @Serializable annotation isn't required 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))
// {"name":"kotlinx.serialization","status":"SUPPORTED"}
}
//sampleEnd
```
{kotlin-runnable="true"}

> On Kotlin/JS and Kotlin/Native, you must use the `@Serializable` annotation for an `enum` class to use as a root object,
> such as in `encodeToString<Status>(Status.SUPPORTED)`.
>
{style="note"}

#### Customize serial names of enum entries

> For more information on customizing serial names, see [Customize serial names](serialization-customization-options.md#customize-serial-names).
>
{style="tip"}

To customize the serial names of enum entries, use the `@SerialName` annotation and mark the enum class with `@Serializable`:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
// Requires the @Serializable annotation because of @SerialName
@Serializable
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))
// {"name":"kotlinx.serialization","status":"maintained"}
}
//sampleEnd
```
{kotlin-runnable="true"}

## Composite types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say we don't need this 'composite types' notion until maybe the chapter about custom serializers. So you can replace this with 'Standard library types'

Alternatively, you should start with the definition of composite types: "Every type which is composed out of the basic types dicussed above, is called composite".
Besides, one can always write a serializer that represents any complex type as string. So I'm in favor of removing 'composite types' notion for now.


Kotlin Serialization supports several composite types from the standard library, but some classes,
such as ranges and the [`Regex`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-regex/) class aren't supported yet.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't say "yet" because we do not have any plans to add the support for them. Maybe saying "but not all, such as ranges and Regex" is sufficient.


### Pair and triple

You can serialize the [`Pair`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/) and [`Triple`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/) classes from the Kotlin standard library:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Project(val name: String)

fun main() {
val pair = 1 to Project("kotlinx.serialization")
println(Json.encodeToString(pair))
// {"first":1,"second":{"name":"kotlinx.serialization"}}
}
//sampleEnd
```
{kotlin-runnable="true"}

### Collections

Kotlin Serialization supports collection types such as [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/), [`Set`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/), and [`Map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rephrase this slightly. The thing is, we also support concrete implementations (e.g. ArrayList and LinkedHashSet), so our users should not have impression that only basic intrefaces are supported. Mutable counterparts should be mentioned as well.

And btw, regular Arrays and primitive Int/Long/...Array too.

Lists and sets are serialized as JSON arrays, and maps are represented as JSON objects.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explicitly mention that this is specific to the Json format. What about:
The way lists and sets are serialized is format specific, but generally mapped to common structures. For example, in Json lists and sets are serialized as ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


#### Serialize lists

Kotlin Serialization serializes [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/) types as JSON arrays.
Here’s an example with a list of classes:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Project(val name: String)

fun main() {
val list = listOf(
Project("kotlinx.serialization"),
Project("kotlinx.coroutines")
)
println(Json.encodeToString(list))
// [{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
}
//sampleEnd
```
{kotlin-runnable="true"}

#### Serialize sets

[`Set`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/) types are serialized as JSON arrays, just like [`List` types](#serialize-lists):

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Project(val name: String)

fun main() {
val set = setOf(
Project("kotlinx.serialization"),
Project("kotlinx.coroutines")
)
println(Json.encodeToString(set))
// [{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
}
//sampleEnd
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Sets have their own section, maybe it is worth mentioning that

Duplicate entries in Sets do not cause exceptions on deserialization by default and behavior on duplicates is implementation-defined.

{kotlin-runnable="true"}

#### Serialize maps

Kotlin serialization supports [`Map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/) types with primitive or enum keys:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
class Project(val name: String)

fun main() {
// Creates a map with Int keys
val map = mapOf(
1 to Project("kotlinx.serialization"),
2 to Project("kotlinx.coroutines")
)
println(Json.encodeToString(map))
// {"1":{"name":"kotlinx.serialization"},"2":{"name":"kotlinx.coroutines"}}
}
//sampleEnd
```
{kotlin-runnable="true"}

In JSON, maps are represented as objects. Since JSON object keys are always strings, keys are encoded as strings even if they are numbers in Kotlin.

> JSON doesn't natively support complex or composite keys.
> To encode structured objects as map keys, see [Encode structured map keys](serialization-json-configuration.md#encode-structured-map-keys).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also add

Other formats may represent Maps with object keys in a more natural ways.

>
{style="note"}
Comment on lines +258 to +261
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention that other formats may support this in different ways.


#### Deserialization behavior of collections

Kotlin uses the declared type to deserialize JSON.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think original

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.

explains what happens better. It also answers the question "What happens if I specify ArrayList or LinkedList"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although you can mention this in the beginning of the section, add the suggested tip to the Set section and drop this section completely.

For example, a `List` preserves duplicates, while a `Set` enforces uniqueness:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
data class Data(
val a: List<Int>,
val b: Set<Int>
)

fun main() {
val data = Json.decodeFromString<Data>("""
{
"a": [42, 42],
"b": [42, 42]
}
""")
// Duplicates are removed from data.b because Set enforces unique elements
println(data)
// Data(a=[42, 42], b=[42])
}
//sampleEnd
```
{kotlin-runnable="true"}

> For more information about collections in Kotlin, see [Collections overview](collections-overview.md).
>
{style="tip"}

## Unit and singleton objects

The Kotlin [`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/) type and other singleton objects are serializable.
A [singleton](object-declarations.md) is a class with only one instance, where the state is defined by the object itself rather than by external properties.
In JSON, singleton objects are serialized as empty structures:

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
object SerializationVersion {
val libraryVersion: String = "1.0.0"
}

fun main() {
println(Json.encodeToString(SerializationVersion))
// {}
println(Json.encodeToString(Unit))
// {}
}
//sampleEnd
```
{kotlin-runnable="true"}

> You can use serialized singleton objects in [closed polymorphic hierarchies](serialization-polymorphism.md#serialize-objects-in-sealed-hierarchies)
> to represent cases without additional fields.
>
{style="tip"}

## Duration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also have two new serializers for Instant btw (#2945). They probably should be mentioned here (and requirement for Kotlin 2.2)


Kotlin's [`Duration`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/) class is serialized to a JSON string using the ISO-8601-2 format:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, this is not json specific.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, maybe just say "string" instead of "JSON string"?


```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.time.*

//sampleStart
fun main() {
val duration = 1000.toDuration(DurationUnit.SECONDS)
println(Json.encodeToString(duration))
// "PT16M40S"
}
//sampleEnd
```
{kotlin-runnable="true"}

## Nothing

The [`Nothing`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-nothing.html) type is serializable by default.
It has no instances, so encoding or decoding it throws an exception.
Use `Nothing` when a type is syntactically required, but not involved in serialization, like in [polymorphic classes with generic base types](serialization-polymorphism.md#serialize-polymorphic-types-with-generic-base-types):

```kotlin
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import kotlinx.serialization.json.*

//sampleStart
@Serializable
sealed class ParametrizedParent<out R> {
@Serializable
data class ChildWithoutParameter(val value: Int) : ParametrizedParent<Nothing>()
}

fun main() {
println(Json.encodeToString(ParametrizedParent.ChildWithoutParameter(42)))
// {"value":42}
}
//sampleEnd
```
{kotlin-runnable="true"}

## What's next

* Dive into the [Serialize classes](serialization-customization-options.md) section to learn how to serialize classes and how to modify the default behavior of the `@Serializable` annotation.
* To explore more complex JSON serialization scenarios, see [JSON serialization overview](configure-json-serialization.md).
* Learn more about polymorphism and serializing different types through a shared base in [Serialize polymorphic classes](serialization-polymorphism.md).