kotlinx.serialization — JetBrains tərəfindən Kotlin obyektlərini JSON, ProtoBuf, CBOR və digər formatlara çevirmək üçün refleksiyasız çoxplatformalı kitabxana. Gson və Moshidən fərqli olaraq, @Serializable annotasiyası vasitəsilə kompilasiya mərhələsində serializator kodu yaradır ki, bu da yüksək performans və tip təhlükəsizliyi təmin edir. GitHub Kotlin/kotlinx.serialization məlumatlarına görə, kitabxana Kotlin/JVM, Kotlin/Native, Kotlin/JS və Kotlin/Wasm dəstəkləyir.
Başlıca
kotlinx.serialization — JetBrains tərəfindən rəsmi Kotlin ekosisteminin bir hissəsi kimi hazırlanmış Kotlin üçün daxili serializasiya kitabxanasıdır. Onun üçüncü tərəf həllərdən (Gson, Moshi, Jackson) əsas fərqi icra zamanı refleksiyadan istifadə etməməsidir. Bunun əvəzinə serializator kodu Kotlin Symbol Processing (KSP) və ya Kotlin Compiler Plugin vasitəsilə kompilasiya mərhələsində yaradılır. Bu, Gson ilə müqayisədə 3-5 dəfəyə qədər performans artımı və tip təhlükəsizliyi təmin edir.
Kitabxana rəsmi olaraq dörd formatı dəstəkləyir: JSON (kotlinx-serialization-json modulu vasitəsilə), ProtoBuf (kotlinx-serialization-protobuf), CBOR (kotlinx-serialization-cbor) və HOCON (kotlinx-serialization-hocon). Formatlar build.gradle.kts faylında ayrı asılılıqlar kimi əlavə edilir ki, bu da layihəyə lazımsız kitabxanaları əlavə etməməyə imkan verir. Hər format üçün öz konfiqurasiya parametrləri dəsti mövcuddur.
Çoxplatformalılıq — kitabxananın əsas xüsusiyyəti. @Serializable ilə eyni sinif bütün hədəf platformalarda işləyir: JVM (Android, Backend), Native (iOS), JS (Web, React) və Wasm (WebAssembly). Tərtibatçı hər platforma üçün fərqli serializasiya tətbiqləri yazmalı deyil — kod vahid qalır. Bu, xüsusilə ümumi kodun Android və iOS arasında bölüşdüyü Kotlin Multiplatform Mobile (KMM) layihələrində dəyərlidir.
Kod yaradılması kotlinx.serialization-da üç mərhələdə baş verir. Birinci mərhələdə Kotlin kompilatoru sinifdə @Serializable annotasiyasını aşkar edir və onu Kotlin Symbol Processing (KSP) plagininə ötürür. İkinci mərhələdə KSP KSerializer interfeysini tətbiq edən serializator obyekti yaradır. Üçüncü mərhələdə yaradılan kod layihənin mənbə kodu ilə birlikdə kompilasiya olunur. Nəticədə, bu mərhələlərdən heç biri tətbiqin işləməsi zamanı yerinə yetirilmir.
Yaradılan serializator sinifin sahələri ilə birbaşa onların getter və setterləri vasitəsilə, refleksiyasız işləyir. Bu o demɗkdir ki, private modifikatoru olan sahələr də @Serializable ilə qeyd olunubsa, serializasiya olunur. Bu yanaşmanın performansı əl ilə serializasiyaya yaxındır: sadə siniflər (5-10 sahə) üçün serializasiya müddəti 10-50 mikrosaniyə, mürəkkəb obyekt qrafları üçün 1000 obyektə 200 mikrosaniyəyə qədərdir.
Android və ya Kotlin/JVM layihəsində kitabxananı qoşmaq üçün build.gradle.kts faylına plagin və asılılıqlar əlavə etmək lazımdır. Kotlin versiyası ilə üst-üstə düşən org.jetbrains.kotlin.plugin.serialization plagini kod yaradılmasını aktivləşdirir. kotlinx-serialization-json kitabxanası dependencies bölməsində Kotlin versiyasından asılı olmayan versiya ilə əlavə edilir.
// build.gradle.kts — kotlinx.serialization qoşulması
plugins {
val kotlinVersion = "2.1.0"
kotlin("jvm") version kotlinVersion
kotlin("plugin.serialization") version kotlinVersion
}
dependencies {
// Əsas serializasiya modulu
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
// Əlavə formatlar
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.7.3")
}
JSON — kotlinx.serialization-da ən məşhur format. Obyekti serializasiya etmək üçün data class üzərində @Serializable annotasiyası yerləşdirmək və Json.encodeToString() çağırmaq kifayətdir. Deserializasiya üçün — Json.decodeFromString() tip göstərilməklə. Kitabxana avtomatik olaraq null sahələri, siyahıları, iç-içə obyektləri və enumları idarə edir. Sinifin bütün sahələri başqa cür göstərilməyibsə, məcburidir.
JSON konfiqurasiyası Json {} builder vasitəsilə həyata keçirilir. Konstruktora deserializasiya zamanı naməlum sahələri ötürmək üçün ignoreUnknownKeys = true, formatlaşdırılmış çıxış üçün prettyPrint = true, səhv qiymətləri defolt qiymətlərə çevirmək üçün coerceInputValues = true ötürülə bilər. Həmçinin encodeDefaults (defolt qiymətləri olan sahələrin serializasiyası) və classDiscriminator (polimorf serializasiya üçün sahə adı) parametrləri mövcuddur.
// JSON serializasiyası və deserializasiyası nümunəsi
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")
)
// prettyPrint ilə JSON serializasiyası
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(project)
println(jsonString)
/*
{
"name": "kotlinx.serialization",
"stars": 7200,
"isActive": true,
"languages": ["Kotlin", "Java"]
}
*/
// JSON-dan deserializasiya
val decoded = json.decodeFromString<Project>(jsonString)
println(decoded.name) // kotlinx.serialization
}
Nümunə əsas dövranı — serializasiya və deserializasiyanı göstərir. @Serializable annotasiyası olan Project data class avtomatik olaraq encodeToString və decodeFromString əldə edir. isActive sahəsinin defolt qiyməti true-dur — əgər JSON-da bu sahə yoxdursa, defolt dəyər istifadə olunur. Əgər ignoreUnknownKeys = true olmadan JSON-da naməlum sahələr gələrsə, SerializationException istisnası atılacaq.
Sealed class — kotlinx.serialization üçün ən güclü istifadə hallarından biri. Kitabxana sealed class iyerarxiyaları üçün əlavə konfiqurasiya olmadan polimorf serializasiyanı dəstəkləyir: sealed class və onun bütün varislərini @Serializable ilə qeyd etmək kifayətdir. Serializasiya zamanı „type” sahəsi (classDiscriminator vasitəsilə konfiqurasiya olunur) əlavə edilir ki, deserializasiya zamanı konkret tip müyən edilir.
// Sealed class polimorf serializasiyası
@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 polimorf serializasiyası xüsusilə API müştərilərində faydalıdır, burada server müxtəlif cavab növləri qaytarır. kotlinx.serialization olmadan diskriminator sahəsinə görə when ilə əl ilə deserializator yazmaq lazım gələrdi. Kitabxana ilə bu bir annotasiya ilə həll edilir. classDiscriminator marker sahəsinin adını (defolt „type”) serverin gözlədiyi istənilən dəyərə dəyişməyə imkan verir.
Kitabxana serializasiyanın incə tənzimlənməsi üçün annotasiyalar dəsti təqdim edir. Əsas annotasiya sinif üçün @Serializable-dır. Əlavələr: JSON-da sahə adını təyin etmək üçün @SerialName (Kotlin adından fərqlidirsə), sahəni serializasiyadan çıxarmaq üçün @Transient, JSON-da mütləq olmalı sahə üçün @Required, defolt qiyməti olan sahənin məcburi serializasiyası üçün @EncodeDefault.
| Annotasiya | Təyinat | Nümunə |
|---|---|---|
| @Serializable | Sinif üçün serializator yaradılmasını aktivləşdirir | @Serializable data class User |
| @SerialName | Sahənin formatda alternativ adını təyin edir | @SerialName(“user_name”) val name: String |
| @Transient | Sahəni serializasiyadan çıxarır | @Transient val cache: MutableMap |
| @Required | Sahə deserializasiya zamanı JSON-da məcburidir | @Required val id: String |
| @EncodeDefault | Sahəni hətta defolt qiymətlə belə serializasiya edir | @EncodeDefault val type: Type = Type.A |
| @Serializer | Sinfə xüsusi serializator əlavə edir | @Serializer(forClass = Date::class) |
@SerialName annotasiyası sahə adlarının snake_case, Kotlin stilinin isə camelCase olduğu API-lərdə işləyərkən kritikdir. Məsələn, server “user_id” göndərir, Kotlin kodunda isə userId istifadə olunur. @SerialName(“user_id”) bu problemi əlavə mapperlər olmadan həll edir. @Transient serverə göndərilməsi lazım olmayan sahələr üçün əlverişlidir — məsələn, müvəqqəti hesablama dəyərləri və ya keş.
Defolt olaraq kotlinx.serialization-da bütün sahələr məcburidir. Əgər sahə JSON-da olmaya bilərsə, onu nullable (String?) etmək və ya defolt qiymət təyin etmək lazımdır (val name: String = “”). Bununla belə, sahənin Kotlin-də nullable olmadığı, lakin API versiyalaşdırma səbəbindən JSON-da olmaya biləcəyi hallar var. Bu halda @Required sahə yox olduqda SerializationException atır, defolt qiymət isə xətasız default-u doldurur.
KSerializer — kotlinx.serialization-da bütün serializatorların tətbiq etdiyi interfeys. Standart kod yaradılması uyğun deyilsə (məsələn, Date, Bitmap və ya xüsusi ikilik formatla iş üçün), öz serializatorunuzu yaza bilərsiniz. Bunun üçün serialize() və deserialize() metodlarını, həmçinin format sxemi üçün descriptor təqdim etmək lazımdır.
Xüsusi serializatorlar iki yolla qoşulur: konkret sinfə bağlamaq üçün @Serializable(with = MySerializer::class) annotasiyası və ya Json { serializersModule = ... } vasitəsilə qlobal olaraq bütün tip nümunələrinə bağlamaq. İkinci üsul daxili tiplər (Date, UUID) üçün üstünlük təşkil edir ki, hər sahədə annotasiya yazmaq lazım olmasın.
// java.util.Date üçün xüsusi serializator
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())
}
}
// Xüsusi serializatordan istifadə
@Serializable
data class Event(
val title: String,
@Serializable(with = DateSerializer::class)
val date: Date
)
fun main() {
val json = Json { prettyPrint = true }
val event = Event("Buraxılış", Date())
println(json.encodeToString(event))
}
Nümunədə DateSerializer java.util.Date-i ISO 8601 sətirinə çevirir. Xüsusi serializator olmadan kotlinx.serialization Date ilə işləyə bilməz — bu, Kotlin standart kitabxanasına daxil olmayan tipdir. Konkret sahədə @Serializable(with = DateSerializer::class) serializatoru yalnız bu sahə üçün qoşur. Bütün Date-lərin qlobal qeydiyyatı üçün Json { serializersModule = SerializersModule { contextual(DateSerializer) } } istifadə edin.
kotlinx.serialization təkcə JSON ilə məhdudlaşmır. Kitabxana dörd daxili formatı dəstəkləyir, hər biri öz modulu və konfiqurasiyası ilə. JSON (kotlinx-serialization-json) — universal, insan tərəfindən oxuna bilən, REST API üçün uyğun. ProtoBuf (kotlinx-serialization-protobuf) — ikilik, yığcam, məcburi sxemlə, yüksək yüklü mikroxidmətlər üçün. CBOR (kotlinx-serialization-cbor) — JSON-un ikilik analoqu, məhdud trafikli IoT və mobil cihazlar üçün əlverişli. HOCON (kotlinx-serialization-hocon) — TypeSafe Config ilə uyğun konfiqurasiya formatı.
| Format | Modul | Tip | Sxem | Tipik tətbiq |
|---|---|---|---|---|
| JSON | kotlinx-serialization-json | Mətn | Könüllü | REST API, məlumat saxlama |
| ProtoBuf | kotlinx-serialization-protobuf | İkilik | Məcburi (.proto) | Mikroxidmətlər, gRPC |
| CBOR | kotlinx-serialization-cbor | İkilik | Könüllü | IoT, mobil cihazlar |
| HOCON | kotlinx-serialization-hocon | Mətn | Könüllü | Konfiqurasiya faylları |
ProtoBuf .proto fayllarında sxemin təyin edilməsini tələb edir, lakin kotlinx-serialization-protobuf Kotlin siniflərini birbaşa @Serializable-dan .proto olmadan yaradır. Bu, inkişafı sadələşdirir: data class-ı annotasiya etmək və ProtoBuf.encodeToByteArray() istifadə etmək kifayətdir. CBOR xüsusilə NFC və ya BLE vasitəsilə yığcam ikilik məlumat ötürmək lazım olduqda Android çərçivəsi üçün aktualdır. CBOR mesajının ölçüsü eyni məlumat dəsti ilə JSON-dan orta hesabla 20-30% kiçikdir.
Mobil tətbiqdə REST API üçün JSON optimaldır — əlavə alətlər olmadan sazlanır, jurnallarda oxunur və istənilən backend ilə uyğundur. Əgər tətbiq mikroxidmətlər arasında böyük həcmdə məlumat ötürürsə (yüzlərlə meqabayt) — ProtoBuf ikilik kodlaşdırma sayəsində 5 dəfəyə qədər sürət qazandırar. Parametrləri fayllarda saxlamaq üçün HOCON və ya JSON istifadə edin. Sərt trafik məhdudiyyəti olan cihazlar (IoT sensorları) üçün — CBOR.
Birinci səhv — deserializasiya zamanı naməlum açarlara məhəl qoymamaq. Əgər server yeni sahə əlavə edibsə və ignoreUnknownKeys = false-dursa, tətbiq SerializationException ilə çəkiləcək. Defolt olaraq bu bayraq söndürülüb. Həll: API dəyişikliklərinə davamlı olmaq üçün istehsal kodunda həmişə Json { ignoreUnknownKeys = true } təyin edin.
İkinci səhv — data class-da internal və ya private sahələrin serializasiyası. Kotlin data class-da primary constructor-dakı bütün sahələr defolt olaraq serializasiya olunur. Əgər sahə həssas məlumat ehtiva edirsə (şifrə, token), onu @Transient ilə qeyd etmək və ya primary constructor-dan çıxarmaq lazımdır. @Transient sahəni JSON-dan tamamilə çıxarır, lakin constructor-da xətaya səbəb ola bilər — belə sahəni @Transient ilə sinif gövdəsində təyin etmək daha yaxşıdır.
Üçüncü səhv — sealed class olmadan polimorf serializasiya. sealed əvəzinə open class istifadə edilərsə, kotlinx.serialization serializersModule-də bütün varislərin açıq qeydiyyatını tələb edir. sealed class-dan fərqli olaraq, kompilator bütün varisləri bilir, open class isə ixtiyari genişləndirməyə imkan verir — kitabxana bütün alt tipləri avtomatik müyən edə bilməz. Qeydiyyat Json { serializersModule = SerializersModule { polymorphic(Base::class) { subclass(Derived::class) } } } vasitəsilə aparılır.
kotlinx.serialization versiyası Kotlin versiyası ilə uyğun olmalıdır. JetBrains uyğunluq cədvəlini dərc edir: kotlinx-serialization 1.6.x Kotlin 1.9.x ilə, 1.7.x Kotlin 2.0.x və 2.1.x ilə uyğundur. Versiyaların uyğunsuzluğu „Symbol ‘serializer’ is missing” kimi anlaşılmaz kompilasiya xətalarına səbəb olur. Həmişə cari versiyanı mavenCentral-də və ya layihənin GitHub repozitoriyasında yoxlayın.
Tez-tez verilən suallar
kotlinx.serialization KSP vasitəsilə kompilasiya zamanı kod yaradılmasından istifadə edir, Gson və Moshi isə icra zamanı refleksiyasından. Bu, performansda üstünlük (Gson-dan 3-5 dəfə sürətli) və tip təhlükəsizliyi təmin edir. Gson annotasiyasız istənilən sahəni serializasiya edir ki, bu da məlumat sızıntısına səbəb ola bilər. kotlinx.serialization açıq @Serializable annotasiyası tələb edir ki, bu da daha təhlükəsizdir. Moshi də codegen-i dəstəkləyir, lakin yalnız JVM və Android üçün.
Bəli, kotlinx.serialization JetBrains-in rəsmi çoxplatformalı kitabxanasıdır. O, Kotlin/JVM (Android, Backend), Kotlin/Native (iOS), Kotlin/JS (Web, React) və Kotlin/Wasm üzərində işləyir. API bütün platformalar üçün vahiddir: @Serializable + Json.encodeToString() hər yerdə eyni işləyir. iOS üçün əlavə tənzimləmə tələb olunmur — Kotlin/Native serializasiya olunmuş kodu yerli ikilik fayla kompilasiya edir.
Nullable sahələr (String?) JSON-da dəyər yoxdursa və ya null göstərilirsə, null kimi deserializasiya olunur. Defolt qiyməti olmayan non-nullable sahələr (String) üçün JSON-da sahənin olmaması SerializationException-a səbəb olur. Null dəyərlərin JSON-a düşməməsini istəyirsinizsə, Json { encodeDefaults = false } konfiqurasiya edin. Bu, default-a bərabər olan bütün sahələri (nullable üçün null daxil olmaqla) çıxışdan çıxaracaq.
@SerialName(“snake_case_name”) istifadə edin adı Kotlin formatından fərqlənən hər sahədə. Alternativ olaraq, Kotlin 2.0+ üçün Json { namingStrategy = JsonNamingStrategy.SnakeCase } mövcuddur — camelCase ↔ snake_case avtomatik çevrilməsi. Bu tənzimləmə bütün sahələrə birdən tətbiq olunur. Qismən fərdiləşdirmə lazımdırsa, @SerialName-i qlobal strategiya ilə birləşdirin.
Xeyr, Flow və korutinlər birbaşa serializasiya olunmur — onlar asinxron icranı təmsil edir, məlumatı deyil. Flow-dan məlumat ötürmək üçün onu korutində .toList() vasitəsilə kolleksiyaya yığmaq və kolleksiyanı serializasiya etmək lazımdır. Eynilə, Job, Deferred və ya Continuation serializasiya oluna bilməz. Yalnız data class-ları — davranış məntiqi olmayan məlumat modellərini serializasiya edin.
Nəticələr
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun