kotlinx.serialization — багатоплатформенна бібліотека от JetBrains для преобразования Kotlin-об'єктів в JSON, ProtoBuf, CBOR и другие формати без використання рефлексії. В отличие от Gson и Moshi, она генерує код серіалізатора на етапі компіляції через анотацію @Serializable, что даёт высокую продуктивність и безпека типів. По данным GitHub Kotlin/kotlinx.serialization, бібліотека підтримує Kotlin/JVM, Kotlin/Native, Kotlin/JS и Kotlin/Wasm.
головне
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.
генерація коду в 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.
// 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 — самый популярний формат в kotlinx.serialization. Для серіалізації об'єкта достаточно навесить на data class анотацію @Serializable и вызвать Json.encodeToString(). Для десеріалізації — Json.decodeFromString() с указанием типу. бібліотека автоматически обрабатывает null-поля, списки, вложенные об'єкти и enums. Все поля класу по умолчанию обязательны, если не указано иное.
налаштування JSON-конфігурації выполняется через Json {} builder. В конструктор можна передать ignoreUnknownKeys = true для пропуска неизвестных полів при десеріалізації, prettyPrint = true для форматированного вывода, coerceInputValues = true для преобразования некорректных значень в значення по умолчанию. також доступны налаштування encodeDefaults (сериализовать поля со значеннями по умолчанию) и classDiscriminator (имя поля для полиморфной серіалізації).
// Приклад серіалізації та десеріалізації 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 — один из самых мощных кейсов kotlinx.serialization. бібліотека підтримує полиморфную серіалізацію для sealed class иерархий без дополнительной налаштування: достаточно позначити sealed class и все его наследники аннотацией @Serializable. При серіалізації добавляется поле "type" (настраивается через classDiscriminator), по которому при десеріалізації определяется конкретный тип.
// Поліморфна серіалізація 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") в любое значення, ожидаемое сервером.
бібліотека предоставляет набор анотацій для тонкой налаштування серіалізації. основна — @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 удобна для полів, які не потрібно отправлять на сервер — наприклад, временные вычисляемые значення или кеши.
По умолчанию все поля в kotlinx.serialization обязательны. Если поле может отсутствовать в JSON, потрібно сделать его nullable (String?) или задать значення по умолчанию (val name: String = ""). Однако бывают ситуации, когда поле не nullable в Kotlin, но его может не быть в JSON из-за версионирования API. В этом случае @Required выбрасывает SerializationException при отсутствии поля, а значення по умолчанию — заполняет default без помилки.
KSerializer — інтерфейс, який реализуют все серіалізаторы в kotlinx.serialization. Если стандартная генерація коду не подходит (наприклад, для роботи с Date, Bitmap, или специфическим бинарным форматом), можна написать свой серіалізатор. Для этого потрібно реализовать методи serialize() и deserialize(), а також предоставить descriptor — описание структуры для схеми формата.
кастомні серіалізаторы подключаются двумя способами: через анотацію @Serializable(with = MySerializer::class) для привязки к конкретному класу или глобально через Json { serializersModule = ... } для привязки ко всем экземплярам типу. Второй способ предпочтительнее для встроенных типів (Date, UUID), чтобы не писать анотацію на каждом поле.
// Кастомний серіалізатор для 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) } }.
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.
| формат | модуль | тип | схема | типичное применение |
|---|---|---|---|---|
| JSON | kotlinx-serialization-json | Текстовый | Опционально | REST API, хранение даних |
| ProtoBuf | kotlinx-serialization-protobuf | Бинарный | Обязательна (.proto) | Микросервисы, gRPC |
| CBOR | kotlinx-serialization-cbor | Бинарный | Опционально | IoT, мобильные устройства |
| HOCON | kotlinx-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.
Первая помилка — игнорирование 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 використовує compile-time генерацію коду через KSP, а Gson и Moshi — runtime-рефлексию. Это даёт перевага в продуктивності (в 3-5 раз быстрее Gson) и типобезпеки. Gson сериализует любое поле без анотації, что может привести к утечке даних. kotlinx.serialization требует явной анотації @Serializable, что безопаснее. Moshi також підтримує codegen, но только для JVM и Android.
Да, kotlinx.serialization — офіційна багатоплатформенна бібліотека JetBrains. Она працює на Kotlin/JVM (Android, Backend), Kotlin/Native (iOS), Kotlin/JS (Web, React) и Kotlin/Wasm. API едино для всех платформ: @Serializable + Json.encodeToString() працює одинаково везде. Для iOS не требуется дополнительных настроек — Kotlin/Native компилирует сериализованный код в нативный бинарник.
Nullable-поля (String?) десеріалізуються как null, если в JSON значення отсутствует или указано null. Для non-nullable полів (String) без значення по умолчанию отсутствие поля в JSON вызовет SerializationException. Если вы хотите, чтобы null-значення не попадали в JSON, настройте Json { encodeDefaults = false }. Это исключит из вывода все поля, равные default (включая null для nullable).
використовуйте @SerialName("snake_case_name") на каждом поле, имя которого отличается от Kotlin-формата. Альтернативно, для Kotlin 2.0+ доступен Json { namingStrategy = JsonNamingStrategy.SnakeCase } — автоматическое преобразование camelCase ↔ snake_case. Эта налаштування применяется ко всем полям сразу. Если нужна частичная кастомизация, комбинируйте @SerialName с глобальной стратегией.
Нет, Flow и корутины не сериализуемы напрямую — они представляют асинхронное выполнение, а не данные. Для передачі даних из Flow потрібно собрать его в коллекцию через .toList() в корутине и сериализовать коллекцию. Аналогично, нельзя сериализовать Job, Deferred или Continuation. Сериализуйте только data class — модели даних без поведенческой логики.
підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також