Go 1.27 正式提供 encoding/json/v2 反序列化效能最多快十倍

Go 團隊於 2026 年 11 月 4 日在官方博客公布,全新的 encoding/json/v2 與 encoding/json/jsontext 兩個套件已結束實驗階段,隨 Go 1.27 正式提供。這是 encoding/json 十多年來最大幅度的改動,官方強調舊有的 v1 API 會繼續支援,開發者毋須強制遷移[1][2]。

語法層與語意層分家

新設計把 JSON 處理拆成兩層。jsontext 只負責語法,即按 RFC 8259 的文法讀寫 JSON token 與 value,不依賴反射;json/v2 則負責語意,把 JSON 值對應到 Go 型別。

jsontext 提供真正串流的 Encoder 與 Decoder,可以逐個 value 或 token 讀寫,寫入端亦有 WriteToken,補回 v1 只能讀 token、不能寫 token 的缺口。json/v2 的函式包括 Marshal、MarshalWrite、MarshalEncode、Unmarshal、UnmarshalRead 與 UnmarshalDecode,全部接受可變的 Options 參數,令設定可以直接傳入函式,亦可沿呼叫堆疊向下傳遞[1]。

型別自訂方面,除了沿用 v1 的 Marshaler 與 Unmarshaler,新增 MarshalerTo 與 UnmarshalerFrom,讓型別直接對 Encoder 或 Decoder 輸出,省去先配置 []byte 再讓套件驗證與重新格式化的開銷。呼叫端亦可以用 WithMarshalers、MarshalFunc 等選項,為任意型別指定序列化方式,優先於型別自身的方法[1]。

預設行為變得更嚴格

v2 的預設行為與 v1 有若干部份不同,官方形容大部分可以透過 struct tag 或 option 還原[1][2]:

  • 遇到無效 UTF-8 會回報錯誤,而非默默接受造成資料損壞。
  • JSON 物件出現重複成員名稱會回報錯誤。官方指出重複名稱在 RFC 8259 中沒有一致解釋,曾被利用作攻擊,例如 CVE-2017-12635。
  • nil slice 與 nil map 分別序列化為 [] 與 {},不再輸出 null。
  • 反序列化改用區分大小寫的欄位配對,避免意外的欄位命中。
  • omitempty 重新定義為「編碼後屬空值」才省略,即 null、""、[]、{}。
  • 序列化 time.Duration 會回報錯誤,因為目前沒有公認的預設表示法,需由呼叫端指定。

效能與實際案例

官方基準顯示,v2 的 Marshal 效能與 v1 大致相若,有時略快、有時略慢;Unmarshal 則明顯較快,基準測試最多有十倍改善。若現有的 Marshaler、Unmarshaler 實作同時改為實作 MarshalerTo、UnmarshalerFrom,可以進一步受益於串流處理。

博客舉例,Kubernetes 有服務在 UnmarshalJSON 內遞迴解析 OpenAPI 規格,效能嚴重受損(kubernetes/kube-openapi 第 315 號議題),改用 UnmarshalJSONFrom 後改善達數個數量級。團隊表示會在 Go 1.28 繼續優化 v2 仍較 v1 慢的情境[1]。

遷移安排與相容性

Go 1.27 起,v1 的 encoding/json 在底層改由 v2 實作,並自動帶入一整組 v1 相容選項,因此現有程式行為不變,亦可即時享有 MarshalJSONTo 等新增能力。若遇到相容問題,可以在建置時設定 GOEXPERIMENT=nojsonv2,回復原本的 v1 實作,但官方預期此退出選項會在未來版本移除[2]。

遷移可以逐個呼叫點進行。開發者可以選擇全套 v2 行為但保留某一項舊行為,例如 json.Marshal(..., json.DefaultOptionsV2(), jsontext.AllowDuplicateNames(true));亦可保留 v1 行為但關閉重複名稱,寫法是 jsonv1.DefaultOptionsV1() 配合 jsontext.AllowDuplicateNames(false)。官方另提供遷移指南與套件文件,列出每項行為差異對應的選項[1]。

這項改動由 2020 年底開始構思,經歷實驗階段與六年打磨,官方特別提到大部分開發者並非任職 Google,反映 Go 生態的協作模式[1]。

參考資料

[1] A new Go API for JSON(2026-11-04)

[2] Go 1.27 Release Notes

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*