Привет, Хабр! Наверное, все мы хотя бы раз сталкивались с задачей сериализации данных, будь то обмен данными с сервером, сохранение состояния объекта в файл или передача данных между различными приложениями. В Kotlin для этих целей удобно использовать библиотеку Kotlinx.serialization, которая позволяет работать с данными, сериализуя и десериализуя их в различные форматы, такие как JSON, CBOR, и Protocol Buffers.
Эта библиотека предоставляет инструменты для сериализации объектов в строки (например, в JSON) и обратно. Главное её преимущество — это полная интеграция с Kotlin.
Чтобы начать работать с Kotlinx.serialization, нужно подключить нужные зависимости в вашем проекте:
Сначала добавим в наш проект Kotlinx.serialization. Для этого нужно подключить Gradle‑плагин и саму библиотеку:
plugins { kotlin("jvm") version "1.8.0" kotlin("plugin.serialization") version "1.8.0" } dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0") }
Теперь есть доступ к необходимым инструментам для работы с сериализацией.
Основы работы с JSON
Начнем с простого примера. Для сериализации объекта в JSON нужно просто использовать аннотацию @Serializable, которая делает класс доступным для сериализации.
Пример сериализации и десериализации объекта:
import kotlinx.serialization.* import kotlinx.serialization.json.* @Serializable data class User(val name: String, val age: Int) fun main() { val user = User("John", 30) // Сериализация val jsonString = Json.encodeToString(user) println("Serialized JSON: $jsonString") // Десериализация val deserializedUser = Json.decodeFromString<User>(jsonString) println("Deserialized User: $deserializedUser") }
Результат:
Serialized JSON: {"name":"John","age":30} Deserialized User: User(name=John, age=30)
Использовали аннотацию @Serializable, чтобы класс стал доступен для сериализации и десериализации. Но это только начало.
Кастомизация сериализации
Бывает, что нужно настроить процесс сериализации под свои нужды. К примеру, если нужно изменять формат поля — например, хранить дату в другом формате или игнорировать некоторые поля в процессе сериализации.
Предположим, есть класс, содержащий поле типа Date. По дефолту Kotlinx.serialization не знает, как сериализовать этот тип. Для этого нужно создать свой кастомный сериализатор:
import kotlinx.serialization.* import kotlinx.serialization.json.* import java.text.SimpleDateFormat import java.util.* @Serializable data class Event(val name: String, @Serializable(with = DateSerializer::class) val date: Date) object DateSerializer : KSerializer<Date> { private val dateFormat = SimpleDateFormat("yyyy-MM-dd") 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()) ?: throw SerializationException("Invalid date format") } } fun main() { val event = Event("KotlinConf", Date()) val jsonString = Json.encodeToString(event) println("Serialized Event: $jsonString") val deserializedEvent = Json.decodeFromString<Event>(jsonString) println("Deserialized Event: $deserializedEvent") }
Результат:
Serialized Event: {"name":"KotlinConf","date":"2025-01-01"} Deserialized Event: Event(name=KotlinConf, date=Thu Jan 01 00:00:00 UTC 2025)
Создали кастомный сериализатор для Date и используем его в классе Event. При сериализации дата будет преобразована в строку с нужным форматом, а при десериализации снова будет преобразована в объект Date.
Полиморфизм в данных
Одной из интересных возможностей Kotlinx.serialization является работа с полиморфными типами. Часто в проектах приходится работать с классами, которые имеют иерархии и наследование, и нужно уметь правильно сериализовать такие структуры данных.
Допустим, есть иерархия классов, и нужно сериализовать разные типы объектов, относящихся к одному базовому классу.
@Serializable sealed class Animal @Serializable @Polymorphic data class Dog(val name: String) : Animal() @Serializable @Polymorphic data class Cat(val name: String) : Animal() fun main() { val animals: List<Animal> = listOf(Dog("Rex"), Cat("Whiskers")) val jsonString = Json.encodeToString(animals) println("Serialized Animals: $jsonString") val deserializedAnimals = Json.decodeFromString<List<Animal>>(jsonString) println("Deserialized Animals: $deserializedAnimals") }
Результат:
Serialized Animals: [{"type":"Dog","name":"Rex"},{"type":"Cat","name":"Whiskers"}] Deserialized Animals: [Dog(name=Rex), Cat(name=Whiskers)]
Как видите, аннотация @Polymorphic позволяет сериализовать объекты различных типов, принадлежащих одному базовому классу. Это мощная фича для работы с полиморфными типами.
Особенности работы с коллекциями и вложенными структурами
Сериализация коллекций объектов — это еще одна часто встречающаяся задача. Kotlinx.serialization отлично работает с коллекциями, включая списки, карты и даже многомерные массивы.
Пример сериализации списка объектов:
@Serializable data class Book(val title: String, val author: String) fun main() { val books = listOf(Book("Kotlin in Action", "Dmitry Jemerov"), Book("Effective Kotlin", "Marcin Moskala")) val jsonString = Json.encodeToString(books) println("Serialized Books: $jsonString") val deserializedBooks = Json.decodeFromString<List<Book>>(jsonString) println("Deserialized Books: $deserializedBooks") }
Результат:
Serialized Books: [{"title":"Kotlin in Action","author":"Dmitry Jemerov"},{"title":"Effective Kotlin","author":"Marcin Moskala"}] Deserialized Books: [Book(title=Kotlin in Action, author=Dmitry Jemerov), Book(title=Effective Kotlin, author=Marcin Moskala)]
Подробнее с serialization ознакомиться можно здесь.
Всем, кому интересна Android разработка, рекомендую посетить открытый урок 27 января, посвященный созданию приложения заметок. Узнать подробнее и записаться можно по ссылке.
