Upgrade to Pro — share decks privately, control downloads, hide ads and more …

encoding/json/v2のUnmarshalはこう変わった:内部実装で見る設計改善

 encoding/json/v2のUnmarshalはこう変わった:内部実装で見る設計改善

Go Conference mini in Sendai 2026 のLT枠「encoding/json/v2のUnmarshalはこう変わった:内部実装で見る設計改善」で発表した登壇資料です。

Avatar for Hiroki Kurasawa

Hiroki Kurasawa

February 21, 2026
Tweet

More Decks by Hiroki Kurasawa

Other Decks in Programming

Transcript

  1. v2のUnmarshal: 4つの改善ポイント 5 func Unmarshal(in []byte, out any, opts ...Options)

    (err error) { dec := export.GetBufferedDecoder(in, opts...) defer export.PutBufferedDecoder(dec) xd := export.Decoder(dec) err = unmarshalDecode(dec, out, &xd.Struct, true) return err } コード 改善ポイント export オブジェクト パッケージ分離 err エラーの原因特定 Get/PutBufferedDecoder オブジェクトの再利用(sync.Pool) Decoder 構造体 効率的なバッファ管理 src/encoding/json/v2/arshal.go
  2. 改善点1: パッケージ分離 v1: encoding/json 1パッケージに全機能が混在 v2: jsontext(構文層)+ json(意味層)に分離 パッケージ 層

    例 jsontext 構文層 { , "name" , : , "Alice" ... json 意味層 "Alice" → User.Name なぜ良いか • 責務が分かれ、変更の影響範囲が限定される • 構文層(jsontext)を単独で利用できる 6
  3. {"name":"Alice","age":"thirty"} → User.Ageはint型なのでエラー v1: cannot unmarshal string into Go struct

    field User.Age v2: cannot unmarshal JSON string into Go int within "/age" 改善点2: エラーの原因特定 なぜ原因特定が容易か • JSONPointerで「どこで」、JSONKindで「何型が」失敗したか分かる &SemanticError{ … JSONPointer: "/age" , // どこで JSONKind: '"', // JSON型(string) GoType: reflect.TypeOf(int(0)), // 期待した型 ... } src/encoding/json/v2/errors.go
  4. • v1: 毎回ゼロ値初期化 → 呼び出しごとにメモリ確保 • v2: GetBufferedDecoder PutBufferedDecoder でプール管理

    なぜ良いか : 高頻度呼び出し時のアロケーションとGCへの負担を削減 改善点3: オブジェクトの再利用 (sync.Pool) 1回目: Unmarshal({"name":"Alice",...}) ↓ Decoder取得 ↓ 処理 ↓ Poolへ戻す 2回目: Unmarshal({"name":"Bob",...}) ↓ 同じDecoder再利用 8
  5. なぜ良いか : buf[prevStart:prevEnd] でコピーなしに前回値へアクセス可能 例: {"name":"Alice","age":30} を "Alice" まで読んだ時 →

    buf[prevStart:prevEnd] = "Alice" decodeBuffer — 1つの []byte を論理的に4分割 改善点4: 効率的なバッファ管理 9 type decodeBuffer struct { … buf []byte // 入力全体を保持するバッファ prevStart int // 前回読んだ値の開始位置 prevEnd int // 前回読んだ値の終了位置 rd io.Reader // 入力元(ストリーミング対応) } src/encoding/json/jsontext/decode.go
  6. まとめ • パッケージ分離 : jsontext + jsonによる責務の分離 • エラーの原因特定 :

    SemanticErrorによる構造化エラー • オブジェクトの再利用 : sync.Poolによるアロケーション削減 • 効率的なバッファ管理 : decodeBufferによるコピーなしアクセス 10