kotlinx.serialization: що це таке, анотації та серіалізація в JSON

Автор: IT Sectr Опубліковано: 2026-03-15 Час читання: 12 хв

kotlinx.serialization — багатоплатформенна бібліотека от JetBrains для преобразования Kotlin-об'єктів в JSON, ProtoBuf, CBOR и другие формати без використання рефлексії. В отличие от Gson и Moshi, она генерує код серіалізатора на етапі компіляції через анотацію @Serializable, что даёт высокую продуктивність и безпека типів. По данным GitHub Kotlin/kotlinx.serialization, бібліотека підтримує Kotlin/JVM, Kotlin/Native, Kotlin/JS и Kotlin/Wasm.

головне

  • kotlinx.serialization — compile-time серіалізація: код генерується на етапі компіляції, рефлексія не використовуєся
  • @Serializable — основна аннотация, яка запускает генерацію серіалізатора для класу
  • Json {} builder — конфігурація JSON через Json { ignoreUnknownKeys = true; prettyPrint = true }
  • багатоплатформенність — бібліотека працює на JVM, Native, JS и Wasm без изменения API
  • кастомні серіалізаторы — через інтерфейс KSerializer для нестандартных форматів даних

Что такое kotlinx.serialization

kotlinx.serialization — это встроенная бібліотека серіалізації для Kotlin, разроботанная JetBrains как часть офіційного Kotlin-экосистемы. Её головне отличие от сторонних решений (Gson, Moshi, Jackson) в том, что она не використовує рефлексию во время виконання. Вместо этого код серіалізатора генерується на етапі компіляції с допомогою Kotlin Symbol Processing (KSP) или Kotlin Compiler Plugin. Это даёт прирост продуктивності до 3-5 раз по сравнению с Gson и гарантирует типобезпека.

бібліотека офіційно підтримує четыре формата: JSON (через модуль kotlinx-serialization-json), ProtoBuf (kotlinx-serialization-protobuf), CBOR (kotlinx-serialization-cbor) и HOCON (kotlinx-serialization-hocon). формати подключаются как отдельные залежності в build.gradle.kts, что позволяет не тащить лишние бібліотеки в проект. Для каждого формата существует свой набор конфигурационных параметрів.

багатоплатформенність — ключевая фича бібліотеки. Один и тот же клас с @Serializable працює на всех целях: JVM (Android, Backend), Native (iOS), JS (Web, React) и Wasm (WebAssembly). розробнику не потрібно писать разные реализации серіалізації для каждой платформы — код остаётся единым. Это особенно ценно в Kotlin Multiplatform Mobile (KMM) проектух, где общий код делится между Android и iOS.

Как працює compile-time генерація коду

генерація коду в kotlinx.serialization происходит в три этапа. На первом етапі компілятор Kotlin обнаруживает анотацію @Serializable на класе и передаёт его Kotlin Symbol Processing (KSP) плагіну. На втором етапі KSP генерує об'єкт-серіалізатор, реализующий інтерфейс KSerializer. На третьем етапі сгенерированный код компілюється вместе с исходным кодом проекту. В результате ни один из этих этапов не выполняется во время роботи приложения.

Сгенерированный серіалізатор працює напрямую с полями класу через их геттеры и сеттеры, без рефлексії. Это означает, что поля с модификатором private також серіалізуються, если они позначені @Serializable. продуктивність такого подхода близка к ручной серіалізації: для простых класов (5-10 полів) время серіалізації составляет 10-50 микросекунд, для сложных графов об'єктів — до 200 микросекунд на 1000 об'єктів.

Для подключения бібліотеки в проект Android или Kotlin/JVM потрібно добавить плагін и залежності в build.gradle.kts. плагін org.jetbrains.kotlin.plugin.serialization версии, совпадающей с версией Kotlin, активирует генерацію коду. бібліотека kotlinx-serialization-json добавляется в раздел dependencies с версией, не зависящей от версии Kotlin.

kotlin
// build.gradle.kts — підключення kotlinx.serialization
plugins {
    val kotlinVersion = "2.1.0"
    kotlin("jvm") version kotlinVersion
    kotlin("plugin.serialization") version kotlinVersion
}

dependencies {
    // Основний модуль серіалізації
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

    // Додаткові формати
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.7.3")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.7.3")
}

Базовое використання: серіалізація в JSON

JSON — самый популярний формат в kotlinx.serialization. Для серіалізації об'єкта достаточно навесить на data class анотацію @Serializable и вызвать Json.encodeToString(). Для десеріалізації — Json.decodeFromString() с указанием типу. бібліотека автоматически обрабатывает null-поля, списки, вложенные об'єкти и enums. Все поля класу по умолчанию обязательны, если не указано иное.

налаштування JSON-конфігурації выполняется через Json {} builder. В конструктор можна передать ignoreUnknownKeys = true для пропуска неизвестных полів при десеріалізації, prettyPrint = true для форматированного вывода, coerceInputValues = true для преобразования некорректных значень в значення по умолчанию. також доступны налаштування encodeDefaults (сериализовать поля со значеннями по умолчанию) и classDiscriminator (имя поля для полиморфной серіалізації).

kotlin
// Приклад серіалізації та десеріалізації JSON
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration

@Serializable
data class Project(
    val name: String,
    val stars: Int,
    val isActive: Boolean = true,
    val languages: List<String> = emptyList()
)

fun main() {
    val project = Project(
        name = "kotlinx.serialization",
        stars = 7200,
        languages = listOf("Kotlin", "Java")
    )

    // Серіалізація в JSON з prettyPrint
    val json = Json { prettyPrint = true }
    val jsonString = json.encodeToString(project)
    println(jsonString)
    /*
    {
        "name": "kotlinx.serialization",
        "stars": 7200,
        "isActive": true,
        "languages": ["Kotlin", "Java"]
    }
    */

    // Десеріалізація з JSON
    val decoded = json.decodeFromString<Project>(jsonString)
    println(decoded.name)  // kotlinx.serialization
}

приклад демонстрирует базовый цикл серіалізації и десеріалізації. Data class Project с аннотацией @Serializable автоматически получает encodeToString и decodeFromString. поле isActive имеет значення по умолчанию true — если в JSON это поле отсутствует, використовуєся default. Если в JSON придут неизвестные поля без ignoreUnknownKeys = true, будет выброшено исключение SerializationException.

Полиморфная серіалізація sealed class

Sealed class — один из самых мощных кейсов kotlinx.serialization. бібліотека підтримує полиморфную серіалізацію для sealed class иерархий без дополнительной налаштування: достаточно позначити sealed class и все его наследники аннотацией @Serializable. При серіалізації добавляется поле "type" (настраивается через classDiscriminator), по которому при десеріалізації определяется конкретный тип.

kotlin
// Поліморфна серіалізація sealed class
@Serializable
sealed class Response

@Serializable
data class Success(val data: String) : Response()

@Serializable
data class Error(val code: Int, val message: String) : Response()

fun main() {
    val json = Json { classDiscriminator = "result_type" }

    val responses: List<Response> = listOf(
        Success(data = "Data loaded"),
        Error(code = 404, message = "Not found")
    )

    val jsonString = json.encodeToString(responses)
    println(jsonString)
    /*
    [
        {"result_type":"Success","data":"Data loaded"},
        {"result_type":"Error","code":404,"message":"Not found"}
    ]
    */

    val decoded = json.decodeFromString<List<Response>>(jsonString)
    when (val first = decoded[0]) {
        is Success -> println("Success: ${first.data}")
        is Error -> println("Error: ${first.code}")
    }
}

Полиморфная серіалізація sealed class особенно полезна в API-клиентах, где сервер возвращает разные типи ответов. Без kotlinx.serialization пришлось бы писать ручной десеріалізатор с when по полю-дискриминатору. С бібліотекою это делается одной аннотацией. classDiscriminator позволяет переименовать поле-маркер (по умолчанию "type") в любое значення, ожидаемое сервером.

анотації kotlinx.serialization: повний огляд

бібліотека предоставляет набор анотацій для тонкой налаштування серіалізації. основна — @Serializable для класу. додаткові: @SerialName для задания имени поля в JSON (если оно отличается от Kotlin-имени), @Transient для исключения поля из серіалізації, @Required для поля, яке обязательно должно присутствовать в JSON, @EncodeDefault для принудительной серіалізації поля со значенням по умолчанию.

АннотацияНазначенняприклад
@SerializableВключает генерацію серіалізатора для класу@Serializable data class User
@SerialNameЗадаёт альтернативное имя поля в формате@SerialName("user_name") val name: String
@TransientИсключает поле из серіалізації@Transient val cache: MutableMap
@Requiredполе обязательно в JSON при десеріалізації@Required val id: String
@EncodeDefaultСериализует поле даже со значенням по умолчанию@EncodeDefault val type: Type = Type.A
@SerializerПривязывает кастомний серіалізатор к класу@Serializer(forClass = Date::class)

Аннотация @SerialName критична при работе с API, где имена полів в snake_case, а Kotlin-стиль — camelCase. Наприклад, сервер присылает "user_id", а в коді Kotlin використовуєся userId. @SerialName("user_id") решает эту проблему без дополнительных мапперов. @Transient удобна для полів, які не потрібно отправлять на сервер — наприклад, временные вычисляемые значення или кеши.

@Required как альтернатива nullable-полям

По умолчанию все поля в kotlinx.serialization обязательны. Если поле может отсутствовать в JSON, потрібно сделать его nullable (String?) или задать значення по умолчанию (val name: String = ""). Однако бывают ситуации, когда поле не nullable в Kotlin, но его может не быть в JSON из-за версионирования API. В этом случае @Required выбрасывает SerializationException при отсутствии поля, а значення по умолчанию — заполняет default без помилки.

кастомні серіалізаторы: KSerializer и ручное управління

KSerializer — інтерфейс, який реализуют все серіалізаторы в kotlinx.serialization. Если стандартная генерація коду не подходит (наприклад, для роботи с Date, Bitmap, или специфическим бинарным форматом), можна написать свой серіалізатор. Для этого потрібно реализовать методи serialize() и deserialize(), а також предоставить descriptor — описание структуры для схеми формата.

кастомні серіалізаторы подключаются двумя способами: через анотацію @Serializable(with = MySerializer::class) для привязки к конкретному класу или глобально через Json { serializersModule = ... } для привязки ко всем экземплярам типу. Второй способ предпочтительнее для встроенных типів (Date, UUID), чтобы не писать анотацію на каждом поле.

kotlin
// Кастомний серіалізатор для java.util.Date
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

object DateSerializer : KSerializer<Date> {
    private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)

    override val descriptor: SerialDescriptor =
        PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: Date) {
        encoder.encodeString(dateFormat.format(value))
    }

    override fun deserialize(decoder: Decoder): Date {
        return dateFormat.parse(decoder.decodeString())
    }
}

// Використання кастомного серіалізатора
@Serializable
data class Event(
    val title: String,
    @Serializable(with = DateSerializer::class)
    val date: Date
)

fun main() {
    val json = Json { prettyPrint = true }
    val event = Event("Release", Date())
    println(json.encodeToString(event))
}

В прикладе DateSerializer преобразует java.util.Date в 'рядківу ISO 8601. Без кастомного серіалізатора kotlinx.serialization не умеет роботать с Date — это тип, не входящий в стандартную бібліотеку Kotlin. @Serializable(with = DateSerializer::class) на конкретном поле подключает серіалізатор только для этого поля. Для глобальной регистрации всех Date використовуйте Json { serializersModule = SerializersModule { contextual(DateSerializer) } }.

формати серіалізації: JSON, ProtoBuf, CBOR, HOCON

kotlinx.serialization не ограничивается JSON. бібліотека підтримує четыре встроенных формата, каждый со своим модулем и конфигурацией. JSON (kotlinx-serialization-json) — универсальный, человекочитаемый, подходит для REST API. ProtoBuf (kotlinx-serialization-protobuf) — бинарный, компактный, с обязательной схемой, для высоконагруженных микросервисов. CBOR (kotlinx-serialization-cbor) — бинарный аналог JSON, удобный для IoT и мобильных устройств с ограниченным трафиком. HOCON (kotlinx-serialization-hocon) — конфигурационный формат, совместимый с TypeSafe Config.

форматмодультипсхематипичное применение
JSONkotlinx-serialization-jsonТекстовыйОпциональноREST API, хранение даних
ProtoBufkotlinx-serialization-protobufБинарныйОбязательна (.proto)Микросервисы, gRPC
CBORkotlinx-serialization-cborБинарныйОпциональноIoT, мобильные устройства
HOCONkotlinx-serialization-hoconТекстовыйОпциональноКонфигурационные файлы

ProtoBuf требует определения схеми в .proto-файлах, но kotlinx-serialization-protobuf генерує Kotlin-класы напрямую из @Serializable без .proto. Это упрощает разработку: достаточно аннотировать data class и использовать ProtoBuf.encodeToByteArray(). CBOR особенно актуален для фреймворка Android, когда потрібно передавать компактные бинарные данные через NFC или BLE. Размер CBOR-сообщения в среднем на 20-30% меньше JSON при том же наборе даних.

Выбор формата для проекту

Для REST API в мобильном приложении оптимален JSON — он отлаживается без дополнительных инструментов, читается в логах и совместим с любым бэкендом. Если приложение передаёт большие объёмы даних между микросервисами (сотни мегабайт) — ProtoBuf даст выигрыш в скорости до 5 раз за счёт бинарной кодировки. Для хранения настроек в файлах використовуйте HOCON или JSON. Для устройств с жёсткими лимитами трафика (IoT-датчики) — CBOR.

типові помилки при работе с kotlinx.serialization

Первая помилка — игнорирование unknown keys при десеріалізації. Если сервер добавил новое поле, а у вас ignoreUnknownKeys = false, приложение упадёт с SerializationException. По умолчанию этот флаг выключен. Решение: всегда задавайте Json { ignoreUnknownKeys = true } для production-коду, чтобы быть устойчивым к изменениям API.

Вторая помилка — серіалізація internal или private полів в data class. В Kotlin data class все поля в primary constructor по умолчанию серіалізуються. Если поле содержит чувствительные данные (пароль, токен), его потрібно позначити @Transient или вынести из primary constructor. @Transient исключает поле из JSON полностью, но в constructor оно может вызвать ошибку — лучше задавать такое поле в теле класу с @Transient.

Третья помилка — полиморфная серіалізація без sealed class. Если использовать open class вместо sealed, kotlinx.serialization требует явной регистрации всех наследников в serializersModule. В отличие от sealed class, где компілятор знает всех наследников, open class допускает произвольное расширение — бібліотека не может автоматически определить все подтипи. Регистрация выполняется через Json { serializersModule = SerializersModule { polymorphic(Base::class) { subclass(Derived::class) } } }.

помилка версионирования бібліотеки

Версия kotlinx.serialization повинна быть совместима с версией Kotlin. JetBrains публикует таблицу совместимости: kotlinx-serialization 1.6.x совместима с Kotlin 1.9.x, 1.7.x — с Kotlin 2.0.x и 2.1.x. Несовпадение версий вызывает cryptic-помилки компіляції "Symbol 'serializer' is missing". Всегда проверяйте актуальную версию на mavenCentral или в GitHub-репозитории проекту.

часто задаваемые питання

Чем kotlinx.serialization отличается от Gson и Moshi?

kotlinx.serialization використовує compile-time генерацію коду через KSP, а Gson и Moshi — runtime-рефлексию. Это даёт перевага в продуктивності (в 3-5 раз быстрее Gson) и типобезпеки. Gson сериализует любое поле без анотації, что может привести к утечке даних. kotlinx.serialization требует явной анотації @Serializable, что безопаснее. Moshi також підтримує codegen, но только для JVM и Android.

підтримує ли kotlinx.serialization Kotlin Multiplatform?

Да, kotlinx.serialization — офіційна багатоплатформенна бібліотека JetBrains. Она працює на Kotlin/JVM (Android, Backend), Kotlin/Native (iOS), Kotlin/JS (Web, React) и Kotlin/Wasm. API едино для всех платформ: @Serializable + Json.encodeToString() працює одинаково везде. Для iOS не требуется дополнительных настроек — Kotlin/Native компилирует сериализованный код в нативный бинарник.

Как обрабатывать null-поля в JSON?

Nullable-поля (String?) десеріалізуються как null, если в JSON значення отсутствует или указано null. Для non-nullable полів (String) без значення по умолчанию отсутствие поля в JSON вызовет SerializationException. Если вы хотите, чтобы null-значення не попадали в JSON, настройте Json { encodeDefaults = false }. Это исключит из вывода все поля, равные default (включая null для nullable).

Что делать, если сервер присылает snake_case поля?

використовуйте @SerialName("snake_case_name") на каждом поле, имя которого отличается от Kotlin-формата. Альтернативно, для Kotlin 2.0+ доступен Json { namingStrategy = JsonNamingStrategy.SnakeCase } — автоматическое преобразование camelCase ↔ snake_case. Эта налаштування применяется ко всем полям сразу. Если нужна частичная кастомизация, комбинируйте @SerialName с глобальной стратегией.

можна ли сериализовать Kotlin Flow или coroutine?

Нет, Flow и корутины не сериализуемы напрямую — они представляют асинхронное выполнение, а не данные. Для передачі даних из Flow потрібно собрать его в коллекцию через .toList() в корутине и сериализовать коллекцию. Аналогично, нельзя сериализовать Job, Deferred или Continuation. Сериализуйте только data class — модели даних без поведенческой логики.

підсумки

  • kotlinx.serialization — compile-time серіалізація через @Serializable, без рефлексії, с продуктивністью до 5 раз выше Gson
  • @Serializable, @SerialName, @Transient — ключевые анотації для налаштування серіалізації полів и класов
  • Json {} builder конфигурирует JSON: ignoreUnknownKeys, prettyPrint, coerceInputValues, encodeDefaults
  • Sealed class и полиморфная серіалізація — бесшовная підтримка иерархий типів без дополнительного коду
  • KSerializer — інтерфейс для кастомних серіалізаторов нестандартных типів (Date, Bitmap, UUID)
  • Четыре формата: JSON, ProtoBuf, CBOR, HOCON — подключаются модулями, API единый для всех
  • багатоплатформенність — единый код для JVM, Native, JS и Wasm; критично для KMM и общих модулей

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також