kotlinx.serialization: ano ito, mga anotasyon at serialisasyon sa JSON

May-akda: IT Sectr Nai-publish: 2026-03-15 Oras ng pagbabasa: 12 min

kotlinx.serialization — isang multiplatform na library mula sa JetBrains para sa pag-convert ng mga Kotlin object sa JSON, ProtoBuf, CBOR at iba pang mga format nang walang paggamit ng reflection. Hindi tulad ng Gson at Moshi, bumubuo ito ng serialiser code sa yugto ng compilation sa pamamagitan ng @Serializable annotation, na nagbibigay ng mataas na pagganap at kaligtasan ng uri. Ayon sa GitHub Kotlin/kotlinx.serialization, sinusuportahan ng library ang Kotlin/JVM, Kotlin/Native, Kotlin/JS at Kotlin/Wasm.

Mga Pangunahing Punto

  • kotlinx.serialization — compile-time serialization: ang code ay nabubuo sa yugto ng compilation, hindi ginagamit ang reflection
  • @Serializable — pangunahing anotasyon na nagpapasimula ng pagbuo ng serialiser para sa klase
  • Json {} builder — configuration ng JSON sa pamamagitan ng Json { ignoreUnknownKeys = true; prettyPrint = true }
  • Multiplatform — gumagana ang library sa JVM, Native, JS at Wasm nang hindi binabago ang API
  • Custom na serialiser — sa pamamagitan ng KSerializer interface para sa hindi karaniwang mga format ng datos

Ano ang kotlinx.serialization

kotlinx.serialization — ay isang built-in na serialization library para sa Kotlin, na binuo ng JetBrains bilang bahagi ng opisyal na Kotlin ecosystem. Ang pangunahing pagkakaiba nito mula sa mga third-party na solusyon (Gson, Moshi, Jackson) ay hindi ito gumagamit ng reflection sa runtime. Sa halip, ang serialiser code ay nabubuo sa yugto ng compilation gamit ang Kotlin Symbol Processing (KSP) o Kotlin Compiler Plugin. Nagbibigay ito ng pagtaas ng pagganap ng hanggang 3-5 beses kumpara sa Gson at ginagarantiyahan ang kaligtasan ng uri.

Opisyal na sinusuportahan ng library ang apat na format: JSON (sa pamamagitan ng modyul na kotlinx-serialization-json), ProtoBuf (kotlinx-serialization-protobuf), CBOR (kotlinx-serialization-cbor) at HOCON (kotlinx-serialization-hocon). Ang mga format ay idinaragdag bilang hiwalay na dependencies sa build.gradle.kts, na nagbibigay-daan sa hindi pag-drag ng mga hindi kinakailangang library sa proyekto. Para sa bawat format, may sariling set ng mga parameter ng configuration.

Multiplatform — pangunahing tampok ng library. Ang parehong klase na may @Serializable ay gumagana sa lahat ng target na platform: JVM (Android, Backend), Native (iOS), JS (Web, React) at Wasm (WebAssembly). Ang developer ay hindi kailangang sumulat ng iba't ibang implementasyon ng serialization para sa bawat platform — ang code ay nananatiling pare-pareho. Ito ay lalong mahalaga sa mga proyekto ng Kotlin Multiplatform Mobile (KMM), kung saan ang shared code ay hinahati sa pagitan ng Android at iOS.

Paano gumagana ang compile-time code generation

Code generation sa kotlinx.serialization ay nangyayari sa tatlong yugto. Sa unang yugto, nakikita ng Kotlin compiler ang @Serializable annotation sa isang klase at ipinapasa ito sa Kotlin Symbol Processing (KSP) plugin. Sa ikalawang yugto, bumubuo ang KSP ng isang serialiser object na nagpapatupad ng KSerializer interface. Sa ikatlong yugto, ang nabuong code ay compile kasama ng source code ng proyekto. Bilang resulta, wala sa mga yugtong ito ang isinasagawa sa panahon ng runtime ng application.

Ang nabuong serialiser ay gumagana nang direkta sa mga field ng klase sa pamamagitan ng kanilang mga getter at setter, nang walang reflection. Nangangahulugan ito na ang mga field na may modifier na private ay serialised din kung minarkahan ng @Serializable. Ang pagganap ng diskarteng ito ay malapit sa manu-manong serialization: para sa mga simpleng klase (5-10 field) ang oras ng serialization ay 10-50 microseconds, para sa mga kumplikadong object graph — hanggang 200 microseconds bawat 1000 object.

Upang ikonekta ang library sa isang Android o Kotlin/JVM project, kailangan mong magdagdag ng plugin at dependencies sa build.gradle.kts. Ang plugin org.jetbrains.kotlin.plugin.serialization sa isang bersyon na tumutugma sa bersyon ng Kotlin ay nagpapagana ng code generation. Ang library na kotlinx-serialization-json ay idinaragdag sa seksyong dependencies na may bersyon na hindi nakadepende sa bersyon ng Kotlin.

kotlin
// build.gradle.kts — pagkonekta ng kotlinx.serialization
plugins {
    val kotlinVersion = "2.1.0"
    kotlin("jvm") version kotlinVersion
    kotlin("plugin.serialization") version kotlinVersion
}

dependencies {
    // Pangunahing modyul ng serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

    // Karagdagang mga format
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.7.3")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.7.3")
}

Pangunahing paggamit: serialization sa JSON

JSON — pinakasikat na format sa kotlinx.serialization. Upang i-serialise ang isang object, ilagay lamang ang @Serializable annotation sa data class at tawagin ang Json.encodeToString(). Para sa deserialization — Json.decodeFromString() na may pagtukoy ng uri. Awtomatikong pinangangasiwaan ng library ang mga null field, listahan, nested object at enum. Ang lahat ng mga field ng klase ay default na sapilitan, maliban kung iba ang tinukoy.

Ang configuration ng JSON ay ginagawa sa pamamagitan ng Json {} builder. Sa constructor, maaaring ipasa ang ignoreUnknownKeys = true para laktawan ang mga hindi kilalang field sa deserialization, prettyPrint = true para sa naka-format na output, coerceInputValues = true para i-convert ang mga maling value sa default na value. Available din ang mga setting na encodeDefaults (serialization ng mga field na may default na value) at classDiscriminator (pangalan ng field para sa polymorphic serialization).

kotlin
// Halimbawa ng JSON serialization at deserialization
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")
    )

    // Serialization sa JSON na may prettyPrint
    val json = Json { prettyPrint = true }
    val jsonString = json.encodeToString(project)
    println(jsonString)
    /*
    {
        "name": "kotlinx.serialization",
        "stars": 7200,
        "isActive": true,
        "languages": ["Kotlin", "Java"]
    }
    */

    // Deserialization mula sa JSON
    val decoded = json.decodeFromString<Project>(jsonString)
    println(decoded.name)  // kotlinx.serialization
}

Ang halimbawa ay nagpapakita ng pangunahing cycle ng serialization at deserialization. Ang data class Project na may @Serializable annotation ay awtomatikong nakakakuha ng encodeToString at decodeFromString. Ang field na isActive ay may default na value na true — kung ang field na ito ay wala sa JSON, ginagamit ang default na value. Kung ang mga hindi kilalang field ay dumating sa JSON nang walang ignoreUnknownKeys = true, itatapon ang SerializationException.

Polymorphic serialization ng sealed class

Sealed class — isa sa pinakamakapangyarihang use case ng kotlinx.serialization. Sinusuportahan ng library ang polymorphic serialization para sa mga sealed class hierarchy nang walang karagdagang configuration: markahan lamang ang sealed class at lahat ng mga subclass nito ng @Serializable. Sa serialization, idinaragdag ang field na „type” (na-configure sa pamamagitan ng classDiscriminator), batay sa kung saan natutukoy ang kongkretong uri sa deserialization.

kotlin
// Polymorphic serialization ng 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}")
    }
}

Ang polymorphic serialization ng sealed class ay lalong kapaki-pakinabang sa mga API client, kung saan ang server ay nagbabalik ng iba't ibang uri ng mga tugon. Kung wala ang kotlinx.serialization, kailangan mong sumulat ng manual deserialiser na may when batay sa discriminator field. Gamit ang library, ito ay ginagawa sa isang anotasyon. Ang classDiscriminator ay nagbibigay-daan sa pagpapalit ng pangalan ng marker field (default „type”) sa anumang value na inaasahan ng server.

Mga anotasyon ng kotlinx.serialization: kumpletong pagsusuri

Ang library ay nagbibigay ng isang set ng mga anotasyon para sa fine-tuning ng serialization. Ang pangunahing ay @Serializable para sa klase. Mga karagdagang anotasyon: @SerialName para sa pagtukoy ng pangalan ng field sa JSON (kung naiiba ito sa pangalan ng Kotlin), @Transient para sa pagbubukod ng field mula sa serialization, @Required para sa field na dapat na naroroon sa JSON, @EncodeDefault para sa sapilitang serialization ng field na may default na value.

AnotasyonLayuninHalimbawa
@SerializableNagpapagana ng pagbuo ng serialiser para sa klase@Serializable data class User
@SerialNameTinutukoy ang alternatibong pangalan ng field sa format@SerialName(“user_name”) val name: String
@TransientInaalis ang field mula sa serialization@Transient val cache: MutableMap
@RequiredField ay sapilitan sa JSON sa deserialization@Required val id: String
@EncodeDefaultSerialise ang field kahit na may default na value@EncodeDefault val type: Type = Type.A
@SerializerIniuugnay ang custom na serialiser sa klase@Serializer(forClass = Date::class)

Ang anotasyon na @SerialName ay kritikal kapag nagtatrabaho sa mga API kung saan ang mga pangalan ng field ay nasa snake_case at ang Kotlin style ay camelCase. Halimbawa, ang server ay nagpapadala ng “user_id” at sa Kotlin code ay ginagamit ang userId. Nilulutas ng @SerialName(“user_id”) ang problemang ito nang walang karagdagang mapper. Ang @Transient ay kapaki-pakinabang para sa mga field na hindi kailangang ipadala sa server — halimbawa, pansamantalang computed value o cache.

@Required bilang alternatibo sa nullable field

Sa default, lahat ng field sa kotlinx.serialization ay sapilitan. Kung ang isang field ay maaaring wala sa JSON, dapat mong gawin itong nullable (String?) o magtakda ng default na value (val name: String = “”). Gayunpaman, may mga sitwasyon kung ang field ay hindi nullable sa Kotlin ngunit maaaring wala sa JSON dahil sa API versioning. Sa kasong ito, ang @Required ay nagtatapon ng SerializationException kapag wala ang field, habang ang default na value ay pinupunan ang default nang walang error.

Custom na serialiser: KSerializer at manu-manong pamamahala

KSerializer — interface na ipinapatupad ng lahat ng serialiser sa kotlinx.serialization. Kung ang standard code generation ay hindi angkop (halimbawa, para sa paggawa sa Date, Bitmap o partikular na binary format), maaari kang sumulat ng sarili mong serialiser. Para dito, kailangan mong ipatupad ang mga pamamaraang serialize() at deserialize(), pati na rin magbigay ng descriptor — paglalarawan ng istraktura para sa schema ng format.

Ang mga custom na serialiser ay ikinokonekta sa dalawang paraan: sa pamamagitan ng anotasyon na @Serializable(with = MySerializer::class) para sa pag-uugnay sa isang partikular na klase o global sa pamamagitan ng Json { serializersModule = ... } para sa pag-uugnay sa lahat ng instance ng uri. Ang pangalawang paraan ay mas gusto para sa mga built-in na uri (Date, UUID) upang hindi magsulat ng anotasyon sa bawat field.

kotlin
// Custom na serialiser para sa 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())
    }
}

// Paggamit ng custom na serialiser
@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))
}

Sa halimbawa, ang DateSerializer ay nagko-convert ng java.util.Date sa isang ISO 8601 string. Kung walang custom na serialiser, hindi gumagana ang kotlinx.serialization sa Date — ito ay isang uri na hindi bahagi ng standard Kotlin library. Ang @Serializable(with = DateSerializer::class) sa isang partikular na field ay nag-uugnay ng serialiser para lamang sa field na iyon. Para sa global registration ng lahat ng Date, gamitin ang Json { serializersModule = SerializersModule { contextual(DateSerializer) } }.

Mga format ng serialization: JSON, ProtoBuf, CBOR, HOCON

kotlinx.serialization ay hindi limitado sa JSON. Sinusuportahan ng library ang apat na built-in na format, bawat isa ay may sariling modyul at configuration. JSON (kotlinx-serialization-json) — universal, nababasa ng tao, angkop para sa REST API. ProtoBuf (kotlinx-serialization-protobuf) — binary, compact, na may sapilitang schema, para sa high-load microservices. CBOR (kotlinx-serialization-cbor) — binary na katumbas ng JSON, maginhawa para sa IoT at mobile device na may limitadong trapiko. HOCON (kotlinx-serialization-hocon) — configuration format, compatible sa TypeSafe Config.

FormatModyulUriSchemaKaraniwang paggamit
JSONkotlinx-serialization-jsonTekstoOpsyonalREST API, pag-iimbak ng datos
ProtoBufkotlinx-serialization-protobufBinarySapilitan (.proto)Microservices, gRPC
CBORkotlinx-serialization-cborBinaryOpsyonalIoT, mobile device
HOCONkotlinx-serialization-hoconTekstoOpsyonalFile ng configuration

ProtoBuf ay nangangailangan ng pagtukoy ng schema sa .proto file, ngunit ang kotlinx-serialization-protobuf ay bumubuo ng mga Kotlin class nang direkta mula sa @Serializable nang walang .proto. Pinapasimple nito ang pag-develop: i-annotate lamang ang data class at gamitin ang ProtoBuf.encodeToByteArray(). Ang CBOR ay lalong mahalaga para sa Android framework kapag kailangan mong magpadala ng compact binary data sa pamamagitan ng NFC o BLE. Ang laki ng CBOR message ay average na 20-30% na mas maliit kaysa JSON para sa parehong set ng datos.

Pagpili ng format para sa proyekto

Para sa REST API sa isang mobile app, ang JSON ay optimal — ito ay nade-debug nang walang karagdagang tools, nababasa sa logs at compatible sa anumang backend. Kung ang app ay naglilipat ng malalaking volume ng data sa pagitan ng microservices (daan-daang megabytes) — ang ProtoBuf ay magbibigay ng pagtaas ng bilis ng hanggang 5 beses dahil sa binary encoding. Para sa pag-iimbak ng mga setting sa file, gamitin ang HOCON o JSON. Para sa mga device na may mahigpit na limitasyon sa trapiko (IoT sensors) — CBOR.

Karaniwang mga pagkakamali sa paggawa sa kotlinx.serialization

Unang pagkakamali — hindi pagpansin sa mga hindi kilalang key sa deserialization. Kung ang server ay nagdagdag ng bagong field at mayroon kang ignoreUnknownKeys = false, ang app ay babagsak na may SerializationException. Sa default, ang flag na ito ay naka-off. Solusyon: palaging itakda ang Json { ignoreUnknownKeys = true } para sa production code upang maging matatag sa mga pagbabago sa API.

Ikalawang pagkakamali — serialization ng internal o private field sa data class. Sa Kotlin data class, lahat ng field sa primary constructor ay default na serialised. Kung ang isang field ay naglalaman ng sensitibong data (password, token), dapat mong markahan ito ng @Transient o alisin mula sa primary constructor. Ang @Transient ay nag-aalis ng field mula sa JSON nang buo, ngunit sa constructor ay maaaring magdulot ng error — mas mainam na tukuyin ang naturang field sa body ng klase na may @Transient.

Ikatlong pagkakamali — polymorphic serialization nang walang sealed class. Kung gumagamit ka ng open class sa halip na sealed, ang kotlinx.serialization ay nangangailangan ng malinaw na rehistrasyon ng lahat ng subclass sa serializersModule. Hindi tulad ng sealed class, kung saan alam ng compiler ang lahat ng subclass, ang open class ay nagbibigay-daan sa arbitraryong pagpapalawak — hindi awtomatikong matutukoy ng library ang lahat ng subtype. Ang rehistrasyon ay ginagawa sa pamamagitan ng Json { serializersModule = SerializersModule { polymorphic(Base::class) { subclass(Derived::class) } } }.

Pagkakamali sa versioning ng library

Ang bersyon ng kotlinx.serialization ay dapat na tugma sa bersyon ng Kotlin. Naglalathala ang JetBrains ng compatibility table: ang kotlinx-serialization 1.6.x ay compatible sa Kotlin 1.9.x, 1.7.x — sa Kotlin 2.0.x at 2.1.x. Ang hindi pagkakatugma ng bersyon ay nagdudulot ng mga nakakalitong compilation error tulad ng „Symbol ‘serializer’ is missing”. Palaging suriin ang kasalukuyang bersyon sa mavenCentral o sa GitHub repository ng proyekto.

Mga Madalas Itanong

Paano naiiba ang kotlinx.serialization sa Gson at Moshi?

kotlinx.serialization ay gumagamit ng compile-time code generation sa pamamagitan ng KSP, habang ang Gson at Moshi ay gumagamit ng runtime reflection. Nagbibigay ito ng kalamangan sa pagganap (3-5 beses na mas mabilis kaysa Gson) at kaligtasan ng uri. Serialise ng Gson ang anumang field nang walang anotasyon, na maaaring humantong sa pagtagas ng datos. Ang kotlinx.serialization ay nangangailangan ng malinaw na @Serializable annotation, na mas ligtas. Sinusuportahan din ng Moshi ang codegen, ngunit para lamang sa JVM at Android.

Sinusuportahan ba ng kotlinx.serialization ang Kotlin Multiplatform?

Oo, ang kotlinx.serialization ay opisyal na multiplatform library ng JetBrains. Ito ay gumagana sa Kotlin/JVM (Android, Backend), Kotlin/Native (iOS), Kotlin/JS (Web, React) at Kotlin/Wasm. Ang API ay pare-pareho para sa lahat ng platform: @Serializable + Json.encodeToString() ay gumagana pareho kahit saan. Para sa iOS, hindi kinakailangan ang karagdagang setting — compile ng Kotlin/Native ang serialised code sa native binary.

Paano hawakan ang mga null field sa JSON?

Nullable field (String?) ay dinedeserialise bilang null kung ang value sa JSON ay wala o tinukoy bilang null. Para sa non-nullable field (String) na walang default na value, ang kawalan ng field sa JSON ay magdudulot ng SerializationException. Kung gusto mong ang mga null value ay hindi pumasok sa JSON, i-configure ang Json { encodeDefaults = false }. Ito ay mag-aalis mula sa output ng lahat ng field na katumbas ng default (kabilang ang null para sa nullable).

Ano ang gagawin kung ang server ay nagpapadala ng snake_case field?

Gamitin ang @SerialName(“snake_case_name”) sa bawat field na ang pangalan ay naiiba sa format ng Kotlin. Bilang alternatibo, para sa Kotlin 2.0+ ay available ang Json { namingStrategy = JsonNamingStrategy.SnakeCase } — awtomatikong conversion camelCase ↔ snake_case. Ang setting na ito ay inilalapat sa lahat ng field nang sabay-sabay. Kung kinakailangan ang bahagyang pagpapasadya, pagsamahin ang @SerialName sa global strategy.

Maaari bang i-serialise ang Kotlin Flow o coroutine?

Hindi, ang Flow at coroutine ay hindi direktang maaaring i-serialise — kinakatawan nila ang asynchronous na pagpapatupad, hindi datos. Upang maglipat ng datos mula sa Flow, dapat mong kolektahin ang mga ito sa isang koleksyon sa pamamagitan ng .toList() sa coroutine at i-serialise ang koleksyon. Katulad nito, ang Job, Deferred o Continuation ay hindi maaaring i-serialise. Serialise lamang ang data class — mga modelo ng datos nang walang behavior logic.

Buod

  • kotlinx.serialization — compile-time serialization sa pamamagitan ng @Serializable, walang reflection, na may pagganap hanggang 5 beses na mas mataas kaysa Gson
  • @Serializable, @SerialName, @Transient — pangunahing anotasyon para sa pag-configure ng serialization ng field at klase
  • Json {} builder nag-configure ng JSON: ignoreUnknownKeys, prettyPrint, coerceInputValues, encodeDefaults
  • Sealed class at polymorphic serialization — walang putol na suporta para sa mga type hierarchy nang walang karagdagang code
  • KSerializer — interface para sa custom na serialiser ng hindi karaniwang uri (Date, Bitmap, UUID)
  • Apat na format: JSON, ProtoBuf, CBOR, HOCON — ikinokonekta sa pamamagitan ng modyul, API pare-pareho para sa lahat
  • Multiplatform — pare-parehong code para sa JVM, Native, JS at Wasm; kritikal para sa KMM at shared modules

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din