Kotlin Serialization (JSON 處理)
在 Kotlin 開發中,處理 JSON 是最常見的任務之一。雖然過去我們常使用 Gson 或 Moshi,但現在官方推薦的解決方案是 kotlinx.serialization。
為什麼選擇 kotlinx.serialization?
- Kotlin First:由 JetBrains 官方開發,完美支援 Kotlin 特性(如 Null 安全、預設值)。
- Multiplatform:支援 JVM, JS, Native, iOS,適合 KMP 專案。
- 零反射 (No Reflection):編譯時生成 Serializer,效能極佳且不需要 ProGuard 設定。
安裝設定
kotlinx.serialization 包含兩個部分:Gradle Plugin 與 Runtime Library。
Build Script (build.gradle.kts):
plugins {
kotlin("jvm") version "1.9.0" // 或 android
// 加入 Serialization Plugin
kotlin("plugin.serialization") version "1.9.0"
}
dependencies {
// 加入 JSON 處理函式庫
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}
標記類別 (@Serializable)
只需在 Data Class 上加上 @Serializable,編譯器就會自動產生序列化所需的程式碼。
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class User(val name: String, val age: Int)
fun main() {
val user = User("Mike", 30)
// 序列化 (Object -> JSON)
val jsonString = Json.encodeToString(user)
println(jsonString)
// 輸出: {"name":"Mike","age":30}
// 反序列化 (JSON -> Object)
val obj = Json.decodeFromString<User>(jsonString)
println(obj)
// 輸出: User(name=Mike, age=30)
}
JSON 設定 (Json Configuration)
預設的 Json 物件非常嚴格。在實務上(特別是串接 API),我們通常需要調整設定來避免崩潰。
建議建立一個全域的 Json 實體:
val json = Json {
// 忽略未知的 Key (非常重要!避免 API 加欄位導致 App 崩潰)
ignoreUnknownKeys = true
// 寬容模式 (允許 Key 不帶引號等非標準 JSON)
isLenient = true
// 輸出格式化 (Pretty Print)
prettyPrint = true
// 如果欄位是預設值,是否需要寫入 JSON (預設為 false 以節省頻寬)
encodeDefaults = true
}
使用方式:
val data = json.decodeFromString<User>(input)
常用 Annotations
自定義欄位名稱 (@SerialName)
當 JSON 的 key (user_name) 與 Kotlin 的變數名 (userName) 不同時使用。這也同時支援多型 (Polymorphism) 的 discriminator 值。
@Serializable
data class User(
@SerialName("user_name")
val userName: String
)
忽略欄位 (@Transient)
如果某個欄位不需要參與序列化,可以使用 @Transient。注意:該欄位必須要有預設值。
@Serializable
data class User(
val name: String,
@Transient
val isLogin: Boolean = false // 不會出現在 JSON 中
)
預設值與可選欄位
如果 JSON 中可能缺少某個欄位,只需在 Kotlin 中提供預設值即可。
@Serializable
data class Response(
val id: Int,
val message: String = "Success" // 如果 JSON 沒給 message,就用這個值
)
泛型支援 (Generic Classes)
kotlinx.serialization 對泛型的支援非常優秀。
@Serializable
data class ApiResponse<T>(
val status: Int,
val data: T
)
fun main() {
val jsonString = """{"status": 200, "data": {"name": "Mike", "age": 30}}"""
// 直接指定泛型型別即可
val response = Json.decodeFromString<ApiResponse<User>>(jsonString)
println(response.data.name) // Mike
}
與 Gson / Moshi 的比較
| 特性 | kotlinx.serialization | Gson | Moshi |
|---|---|---|---|
| 原理 | 編譯時生成 (Compiler Plugin) | 執行時反射 (Reflection) | 兩者皆可 |
| Kotlin 支援 | 完美 (Null 安全、預設值) | 普通 (需額外處理 Null) | 優秀 |
| 效能 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| 多平台 (KMP) | ✅ 支援 | ❌ 僅 JVM | ❌ 僅 JVM |
結論:如果是全新的 Kotlin 專案,請無腦選擇 kotlinx.serialization。