Skip to content

kotlinx.serialization

Serialization & FormatsSerializationKotlin

What it is

Kotlin's official serialisation library, generating encoders at compile time with a compiler plugin — no reflection, and Multiplatform-compatible.

Mark classes @Serializable and the plugin generates the serialiser. Json is configurable for lenient parsing, defaults and unknown keys.

Installation

plugins { kotlin("plugin.serialization") version "2.0.0" }

Getting started

The smallest useful thing you can do with it, and what each part means.

Serialisable classes
@Serializable
data class Book(
    val id: Int,
    val title: String,
    @SerialName("published_year") val year: Int,
    val tags: List<String> = emptyList(),
    val subtitle: String? = null,
)

val json = Json {
    ignoreUnknownKeys = true       // tolerate new fields from the server
    encodeDefaults = false         // omit values equal to their default
    explicitNulls = false          // omit nulls rather than writing null
}

val text = json.encodeToString(book)
val parsed = json.decodeFromString<Book>(text)
ignoreUnknownKeys is essential against a server you do not control — the default is to throw when an unexpected field appears, which breaks clients on every API addition.

Advanced usage

Where the library earns its place over a simpler alternative.

Polymorphism and custom serialisers
@Serializable
@JsonClassDiscriminator("type")
sealed class Event {
    @Serializable @SerialName("click")
    data class Click(val x: Int, val y: Int) : Event()

    @Serializable @SerialName("key")
    data class KeyPress(val key: String) : Event()
}

object InstantSerializer : KSerializer<Instant> {
    override val descriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
    override fun serialize(encoder: Encoder, value: Instant) =
        encoder.encodeString(value.toString())
    override fun deserialize(decoder: Decoder): Instant =
        Instant.parse(decoder.decodeString())
}

@Serializable
data class Order(@Serializable(with = InstantSerializer::class) val placedAt: Instant)
Sealed classes serialise as discriminated unions, and the compiler enforces exhaustive handling on the Kotlin side — schema and code stay aligned by construction.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Encountered an unknown key
Set ignoreUnknownKeys = true, or add the field to the class.
Serializer has not been found for type
The class lacks @Serializable, or it is a third-party type needing a custom serialiser or @Contextual.

Best practices

  • Set ignoreUnknownKeys = true for any API you do not control.
  • Configure one Json instance and reuse it; constructing it per call is wasteful.
  • Use @SerialName rather than renaming Kotlin properties to match the wire format.
  • Prefer this over Jackson for Multiplatform and Android; Jackson remains fine on JVM-only servers.

Background

Why it exists, and what it was reacting to.

Jackson and Gson rely on reflection, which does not exist on Kotlin/Native and costs startup time on Android. kotlinx.serialization generates the code instead, and understands Kotlin's null safety and default values properly.