JSON 轉 Go 結構體轉換器

貼上一個 JSON 物件(或 JSON 陣列),自動生成對應的 Go struct 定義及 json 標籤。

從 JSON 產生 Go 結構體

要在 Go 裡接收 API 回應,就得寫出與該 JSON 對應的結構體及其 json 標籤。欄位只有幾個時並不費事,可一旦巢狀加深,邊想型別名稱邊順著層級走,本身就成了負擔。 本工具只需您貼入 JSON,便會連同巢狀結構一併產生整套定義。

不過請記住,產出的是「由一份樣本推斷出的型別」。 樣本中未出現的鍵自然不會有,而值為 null 的欄位會變成 interface{},因為 Go 並無可對應的可空基本型別。若陣列的元素全是物件,則各元素的鍵會合併為一個結構體,只在部分元素中出現的鍵會被標上 omitempty。 要把輸出變成可用於生產的結構體,前提是您會拿這些推斷去對照真正的 API 規格並加以訂正。

轉換的步驟

  1. 貼入 JSON 物件或陣列皆可,API 回應可原樣貼入。
  2. 給根型別命名 初始為 Root。巢狀型別的名稱由各屬性名轉為帕斯卡命名法自動得出。
  3. 查看產生的結構體 定義會按層級順序排列,json 標籤一併就位。
  4. 複製出來再整理 先過一遍 gofmt,再依真實規格訂正變成 interface{} 的地方,以及該用指標的地方。

用好本工具的小技巧

  • 當陣列的所有元素都是物件時,會合並各元素的鍵生成一個 struct。僅存在於部分元素中的鍵會自動加上 omitempty 標籤。
  • 根型別名稱預設是 "Root",可以改成你喜歡的名字。巢狀物件的 struct 名稱會根據屬性名自動轉換為 PascalCase 生成。
  • 由於 Go 沒有原生的可空基本型別,JSON 的 null 會被轉換為 interface{}。如需更嚴格的處理,可以考慮改用指標型別。
  • 生成結果只是簡單對齊列寬的草稿,粘貼後通過 gofmt 處理即可對齊到專案的標準風格。
  • 直接貼上 API 響應的示例 JSON,即可快速得到 Go 中響應結構體的初稿。

這些情境會用到

著手寫 API 用戶端時

把文件裡的範例回應貼上,馬上就有一份接收用結構體的初稿。

讀取設定檔時

設定以 JSON 保存時,可把其結構直接謄寫成型別。

讀懂第三方規格時

層層巢狀的 JSON,一旦攤開成結構體便容易把握得多。

準備測試夾具時

先由真實回應推出型別,寫模擬資料時便少了張冠李戴之虞。

Go 型別對應的術語

結構體
Go 中把若干欄位聚在一起的型別,對應 JSON 裡的物件。
json 標籤
寫在欄位之後、形如 json:"user_id" 的註解,它告訴編碼器 Go 的欄位名與 JSON 的鍵如何對齊。
omitempty
寫在 json 標籤中的選項,當值為空(false、0、空字串、nil、長度為 0 的切片或映射)時,從輸出中省略。 需要注意結構體型別的欄位即使是零值也不會被省略,而且它並不表示可為 null。
interface{}
可接納任意值的型別。JSON 的 null 與型別多變的欄位會落到這裡,因而成了產生後需回頭查看之處的標記。
帕斯卡命名法
每個單字首字母大寫的寫法,如 UserId。在 Go 中這個開頭的大寫還兼有從套件中匯出的含義。
指標型別
寫作 *string 的型別。當「沒有值」必須與「空字串」區分開來時,便要用它。

常見問題

手工從示例響應編寫 struct 既耗時又容易出錯,尤其在巢狀層級較深時,容易寫錯欄位名轉換或漏寫 json 標籤。自動生成可以避免這些錯誤,大幅縮短開發時間。

JSON 鍵(snake_case 或 camelCase)會被轉換為首字母大寫的 PascalCase,成為 Go 中可匯出的欄位名。原始的 JSON 鍵會原樣保留在 json 標籤(json:"原始鍵")中,因此編碼解碼不受影響。以日文等無法轉為大寫的字元開頭的鍵會加上 Field 前綴(例如 名前 → Field名前),轉換後名稱相同的鍵(如 user_id 與 userId)則在末尾加上序號,確保產生的名稱互不重複、能在 Go 中編譯通過。

null 值會被輸出為 interface{} 型別。僅在陣列部分元素中出現的鍵會自動帶上 omitempty 標籤,但 Go 型別本身只會取零值,如需區分"缺失"與"零值",可以考慮改用指標型別。

JSON 數值中含有小數點或指數(1.0、1e3)時判斷為 float64,兩者都沒有時判斷為 int。Go 的 encoding/json 無法把含小數點或指數的數值讀入整數型別,因此這樣區分。超出 64 位元環境下 int 範圍(約 ±9.2×10^18)的整數會設為 float64,以免讀取失敗。不過 float64 會捨入位數,如需精確處理較大的 ID 等,請考慮改用 json.Number 或 string。

由於 Go 沒有聯合型別,同一個鍵在不同元素中型別不一致時會回退為 interface{},需要在執行時用型別斷言來判斷實際的值型別。
工具君

閒話 ― Go 中 struct 與 json 標籤的設計

Go 是一門靜態型別語言,在處理 JSON 時,標準庫的 encoding/json 包依賴 struct 欄位與 json 標籤之間的對應關係來完成編碼和解碼。手工為外部 API 的響應結構編寫 struct 是一項隨著欄位增多而愈發繁瑣的重複勞動,這在許多 Go 專案中反覆出現。

這款工具通過解析示例 JSON 的結構,自動生成對應的 struct 定義和 json 標籤,從而減輕這類重複工作。同類工具中,支援轉換為多種語言型別的 quicktype 頗為知名;但如果只是想貼上一份示例 API 響應、快速得到一個 Go struct,專注單一功能的工具也自有其便利之處。

由於 Go 沒有讓 struct 欄位變為"可選"的語言特性,對於 JSON 中可能缺失的欄位,常見做法是在 json 標籤中加上 omitempty,或改用指標型別(如 *string)來區分零值與"值不存在"。生成的 struct 終究只是根據結構做出的機械推斷,將可空性判斷和 API 未來變化納入設計,應當對照實際的介面文件手動調整,這是實踐中的通行做法。