Android JSON 解析 (Serialization / Deserialization)

在與後端 API 溝通時,JSON 是最常見的資料格式。我們需要將 JSON 字串轉換為 Kotlin 物件 (Deserialization),或反之 (Serialization)。

在 Android 上有三個主流的選擇:Gson, Moshi, Kotlin Serialization

選手比較

  • Gson (Google):老牌,使用 Java Reflection。優點是相容性好,缺點是效能較差,且對 Kotlin 的 Null Safety 支援不佳(可能把 null 塞給不可空的變數)。已不推薦新專案使用。
  • Moshi (Square):Gson 的繼承者,對 Kotlin 支援較好。
  • Kotlin Serialization (JetBrains):官方推出的庫。完全基於 Kotlin 編譯器插件,不使用 Reflection,效能最好,且完全支援 Kotlin 語法特性 (如預設值、Null Safety)。強烈推薦

使用 Kotlin Serialization

安裝

這需要同時加入 Plugin 與 Library。

// build.gradle.kts (Project)
plugins {
    kotlin("plugin.serialization") version "1.9.0" apply false
}

// build.gradle.kts (App)
plugins {
    kotlin("plugin.serialization")
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
    // 如果配合 Retrofit,需要 converter
    implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
}

標註資料類別

在 data class 上加上 @Serializable

import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class User(
    val id: Int,
    val name: String,
    
    // JSON 欄位名與變數名不同時使用 @SerialName
    @SerialName("avatar_url") 
    val avatarUrl: String? = null, // 支援預設值與 Nullable
    
    // 使用 @Transient 忽略此欄位 (不參與序列化)
    @Transient
    val isSelected: Boolean = false 
)

基本操作

// 序列化 (Object -> JSON)
val user = User(1, "Mike", "http://...")
val jsonString = Json.encodeToString(user)
// 輸出: {"id":1,"name":"Mike","avatar_url":"http://..."}

// 反序列化 (JSON -> Object)
val userObj = Json.decodeFromString<User>(jsonString)

寬容模式 (Lenient) 與 忽略未知欄位

通常 API 會回傳一些我們不需要的欄位,或者格式不標準。我們需要設定 Json 物件。

val jsonConfig = Json {
    ignoreUnknownKeys = true // 忽略 JSON 中多餘的欄位 (非常重要!)
    coerceInputValues = true // 容錯處理
    isLenient = true // 允許寬鬆的 JSON 格式
    prettyPrint = true // 輸出排版後的 JSON
}

// 在 Retrofit 中使用
// .addConverterFactory(jsonConfig.asConverterFactory("application/json".toMediaType()))

小結

擁抱 Kotlin Serialization。它快、安全、且是專為 Kotlin 量身打造的。別忘了都要開啟 ignoreUnknownKeys = true,以免後端加了新欄位導致你的 App 崩潰。