Привет, Хабр! Наверное, все мы хотя бы раз сталкивались с задачей сериализации данных, будь то обмен данными с сервером, сохранение состояния объекта в файл или передача данных между различными приложениями. В 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 января, посвященный созданию приложения заметок. Узнать подробнее и записаться можно по ссылке.
osigida
а что на счет более глубокой иерахии классов?