這幾天我們介紹的 Coroutine、Flow 都是比較可以廣泛使用的工具,Kotlin 作為一個 general purpose programming language,在這方面的確是很強大,但 Kotlin 也是有很多 library 可以完成比較具體的任務的,比如說今天要介紹的 serialization。
Kotlinx Serialization 官網:
https://github.com/Kotlin/kotlinx.serialization
通常做網路傳輸或是儲存資料的時候,我們就會需要 serialization 來做序列化的動作把一個物件轉成可交換保存的 json 或 binary 形式,像是 Gson、Moshi 等 library 等都是很這方面很知名的工具,而在 KMM 的世界裡,則推薦直接使用 Kotlin 出的 kotlinx.serialization。
首先必須先加上 kotlinx.serialization 的 plugin 如下:
plugins {
kotlin("jvm") version "1.7.10" // or kotlin("multiplatform") or any other kotlin plugin
kotlin("plugin.serialization") version "1.7.10"
}
接下來就是要宣告 dependency 如下:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0")
}
Serialization 的使用通常很單純,以 json 格式為例就只有把物件轉成 json 、跟把 json 轉成物件二種,一個簡單的範例如下:
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Project(val name: String, val language: String)
fun main() {
// Serializing objects
val data = Project("kotlinx.serialization", "Kotlin")
val string = Json.encodeToString(data)
println(string) // {"name":"kotlinx.serialization","language":"Kotlin"}
// Deserializing back into objects
val obj = Json.decodeFromString<Project>(string)
println(obj) // Project(name=kotlinx.serialization, language=Kotlin)
}
一般使用就是這麼簡單,但細心的讀者可能會發現有個淺規則要求 json 的 key 跟 object 的 name 要相同,但如果我們想要把二個不同的名字連結在一起的話怎麼辦呢?其實只要冠上 @SerialName
到你的變數前面,你就可以連結二個不同的元素再一起囉,改寫 Project 物件如下也是可以正常運作的:
@Serializable
data class Project(val name: String, @SerialName("language") val lang: String)
通常 serialization 的使用在 Android 裡都會需要在 proguard 做額外的設定,kotlinx.serialization 也不例外,目前建議的修改如下:
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
# static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
# static <1>$$serializer INSTANCE;
#}
除了本篇介紹的 json 格式之外,其實 serialization 也支援以下幾種格式:
只是目前除了 json 外都是處於 experimental 的狀態,是否使用就靠讀者自行判斷囉!
https://github.com/Kotlin/kotlinx.serialization/blob/master/formats/README.md
講完了 serialization 接下來我們就可以開始進入網路傳輸的世界囉,明天見!