跳至內容

JSON 轉 Kotlin

從 JSON 範例生成 Kotlin data class。

輸入

這個工具的用途

從 JSON 樣本產生 Kotlin data class 宣告。不可變模型使用 val 屬性、 樣本中為 null 的欄位使用 nullable 型別(String?), 不是合法 Kotlin 識別字的 JSON 鍵會加上 Gson @SerializedName annotation。使用 package 選項 設定 package 宣告。底層使用 quicktype,完全在你的瀏覽器內運作。

使用步驟

貼上 JSON(或載入範例),即可在右側讀到 Kotlin data class。在選項 面板設定 package name 對應你的模組布局;預設輸出對不是合法 Kotlin 識別字的 JSON 鍵使用 Gson @SerializedName

輸入: {"id":42,"name":"devsmiths","createdAt":"2024-03-11T08:24:00Z","stars":1280,"public":true,"contributors":[{"login":"ada","commits":51,"admin":true},{"login":"linus","commits":33,"admin":false}],"homepage":null}

輸出(Kotlin):

package com.devsmiths

import com.google.gson.annotations.SerializedName

data class Root(
    val id: Long,
    val name: String,
    @SerializedName("createdAt") val createdAt: String,
    val stars: Long,
    val public: Boolean,
    val contributors: List<Contributor>,
    val homepage: String? = null,
)

data class Contributor(
    val login: String,
    val commits: Long,
    val admin: Boolean,
)

限制與邊界情況

  • 輸出採 data class 配 Gson @SerializedName。 要改用 kotlinx.serialization,把 @SerializedName 換成 @SerialName、在每個 class 上加 @Serializable、 import kotlinx.serialization.*
  • 屬性為 val(不可變)。需要 var(例如配合 Android DataBinding)時請手動改——產生器偏向不可變, 因為多數 JSON DTO 解析後應該唯讀。
  • Nullable 型別對樣本中為 null 的欄位用 String?。 搭配預設值(val homepage: String? = null)能讓 Gson 與 Moshi 在缺漏鍵時優雅處理。
  • 整數一律 Long,與大小無關。小 ID 可手動改 Int; Gson 與 Moshi 兩者皆可解碼。浮點預設 Double
  • 日期字串保持為 String,不是 java.time.Instant kotlinx.datetime.Instant。若需型別化日期, 為 Gson 加 TypeAdapter 或為 kotlinx.serialization 加 KSerializer
  • 單樣本推論:樣本中不存在的鍵不會自動變 nullable。all optional 會讓每個欄位變 T? = null

常見問題

為什麼 Gson @SerializedName 而不是 kotlinx.serialization?
Gson 在 Android 仍最常見(許多現有 app 都帶它)且在 JVM 上無需額外設定即可運作。kotlinx.serialization 極佳——強型別、可多平台、由 KSP 驅動——但需要在 build 加上 plugin 並在每個 class 上 `@Serializable`。要切換:把 `@SerializedName("foo")` 換成 `@SerialName("foo")`、為每個 class 加 `@Serializable`、import `kotlinx.serialization.*`。data class 形狀完全相同。
data class 的 val——但我需要可變欄位,怎麼辦?
手動把 `val` 改 `var`。產生器偏向不可變,因為多數 JSON DTO 解析後即唯讀——你以 `copy(fieldName = newValue)` 取代就地變動。Android two-way DataBinding 確實需要 var;改相關欄位即可。
如何為缺漏鍵獲得 null-safe 反序列化?
結合 nullable 型別與預設值:`val homepage: String? = null`。Gson 與 Moshi 在型別為 nullable 且有預設值時,會把缺漏鍵視為 null。沒有預設值時,缺漏鍵會在 construction 時拋錯(Kotlin 的 null-safety 不允許未初始化的非 null 屬性存在)。「all optional」會在每個 nullable 欄位上輸出 `= null` 預設值。
Long vs Int — 為什麼全用 Long?
與 Java 同理:JSON 數字經 JavaScript 為 64-bit double,2^53 以下的整數精確;再大就會在資料到達 Kotlin 前失去精度。Long 涵蓋安全範圍且不會意外 overflow。對你確定放得下 Int 的 ID 可手動收緊;Gson 與 Moshi 兩者皆能解碼。
產生的 data class 可以配 Room / Realm 使用嗎?
Room 可以但有條件。Room 需要 `@Entity` annotation 與無參 constructor——data class 只在每個屬性都有預設值時才有。請加 `@Entity` 並為非 null 屬性提供預設值(`val id: Long = 0`)以滿足 Room。Realm 的話,產生的 class 必須繼承 RealmObject 並使用 var 屬性;請將它們作為 DTO,再映射為 RealmObject。
如何用產生的 data class 解析 JSON?
Gson:`Gson().fromJson(jsonString, Root::class.java)`。Moshi:`moshi.adapter(Root::class.java).fromJson(jsonString)`。kotlinx.serialization(改裝後):`Json.decodeFromString<Root>(jsonString)`。三者皆會遵循 data class 上的 annotation。

內容審閱者: