encoding/json/v2で何が変わる? - v1からv2への変化を徹底比較
glassmonkeyさんの発表
元々phperとのこと
/icons/hr.icon
v1の課題
動作上の課題
重複キーの受理
最後の値のみ残るのでセキュリティ上の脆弱性
権限の上書きなど
無効UTF-8の許容
データ破損リスクを招く
大文字小文字無視
v1では区別しないので解釈の不一致を生む要因になる
map/sliceのゼロ値
jsonを扱う際の直感的な挙動は[](空配列だが、デフォルト挙動のnilが適用されていた
一貫性の欠落
ポインタレシーバのメソッド実装で一貫性が失われる場合がある
APIの扱いにくさ
io.Readerからの正確なアンマーシャルが困難
json.NewDecoder(r).Decode(v)では入力末尾のゴミデータを拒否できない
オプション設定の柔軟性不足
EncoderとDecoderのオプションがMarshalやUnmarshalで使用不可
ユーティリティ関数の柔軟性不足
CompactやIndent、HTMLEscape関数は柔軟性に欠ける出力設計
入力は[]byteで出力は*bytes.Buffer固定なので、入出力とも丸ごとメモリに載せる前提。そのため、巨大データがそもそも扱いづらい
パフォーマンス制約
ストリーミング非対応
全JSONをバッファリングしなきゃいけないので、巨大JSONの場合にメモリに優しくない
強制メモリアロケーション
MarshalJSONが必ず[]byteを返し、既存バッファを再利用できないので強制割り当て(強制アロケーション)が起こる
二度読み・先読み強制
UnmarshalJSONは`初めに1つのJSONの終わり(境界値)を検出し、構造体にパースする際にもう一度JSONを読まなければいけれない
特にネストした構造体で、各レベルでMarshalJSON/UnmarshalJSONを実装すると、この問題が顕著に表れる可能性が高い
v2のコンセプト
構文機能と意味機能に分離している
構文機能(jsontext)
JSONの文法に基づく処理に特化
エンコードのみを
意味機能(json/v2)
JSONとGo値の相互変換を定義
v2
2層に分かれる
構文機能
jsontex
トークンベースのローレベル操作
RFC8259に厳密に準拠した検証
位置情報を保持した詳細なエラー報告
複雑なjsonでもどこが原因かわかりやすい
意味機能
json/v2
ストリーミング方式採用しているので性能向上が実証されている
逐次的に処理できる
オプションを伝搬できる
例えば、出力フォーマットにインデントを付与するオプションなど
破壊的変更も最小限
structタグが破壊的
おまじないなしで、writeやreadを行えるようになった
変わらないもの
MarshalやUnmarshalの使い心地は概ね同じ
v2もstructタグの使用方法も継承している
新しいもの
新しいタグ機能
カスタム実装の必要性を大幅に減らしている
移行の段階的ステップ
ステップ1:v1互換モードでの検証
v2が提供するv1互換機能を使用して、既存コードに対する互換性を確認
GOEXPRERIENT=...
ステップ2:importパスをv2に書き換える
ステップ3:破壊的変更への対応
ゼロ値の扱いなど...
基本的にv1よりv2の方がパフォーマンスに優れている
unsafe使用なし
/icons/hr.icon
資料