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. У трећој фази, генерисани код се компајлира заједно са изворним кодом пројекта. Као резултат, ниједна од ових фаза се не извршава током рада апликације.

Генерисани серијализатор ради директно са пољима класе преко њихових гетера и сетеријa, без рефлексије. То значи да се поља са модификатором 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-у. За серијализацију објекта довољно је ставити анотацију @Serializable на data class и позвати Json.encodeToString(). За десеријализацију — Json.decodeFromString() са навођењем типа. Библиотека аутоматски обрађује null поља, листе, угнежђене објекте и enum-ове. Сва поља класе су подразумевано обавезна, осим ако није другачије назначено.

Подешавање 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-у, користи се подразумевана вредност. Ако у 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("Издање", 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

Прва грешка — игнорисање непознатих кључева при десеријализацији. Ако је сервер додао ново поље и имате ignoreUnknownKeys = false, апликација ће пасти са SerializationException. Подразумевано је ова заставица искључена. Решење: увек постављајте Json { ignoreUnknownKeys = true } за продукцијски код да бисте били отпорни на промене API-ја.

Друга грешка — серијализација internal или private поља у data class-у. У Kotlin data class-у сва поља у primary конструктору се подразумевано серијализују. Ако поље садржи осетљиве податке (лозинку, токен), треба га означити са @Transient или избацити из primary конструктора. @Transient искључује поље из JSON-а у потпуности, али у конструктору може изазвати грешку — боље је такво поље дефинисати у телу класе са @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. Неподударање верзија изазива криптичне грешке компилације попут „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 и корутине нису директно серијализујуће — one представљају асинхроно извршавање, а не податке. За пренос података из 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође