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

Комментарии (1)


  1. osigida
    22.01.2025 20:17

    а что на счет более глубокой иерахии классов?