- glassmonkeyさんの発表
- 元々phperとのこと
v1の課題
動作上の課題
- 重複キーの受理
- 最後の値のみ残るのでセキュリティ上の脆弱性
- 権限の上書きなど
- 最後の値のみ残るのでセキュリティ上の脆弱性
- 無効UTF-8の許容
- データ破損リスクを招く
- 大文字小文字無視
- v1では区別しないので解釈の不一致を生む要因になる
- map/sliceのゼロ値
- jsonを扱う際の直感的な挙動は
[](空配列だが、デフォルト挙動のnilが適用されていた
- jsonを扱う際の直感的な挙動は
- 一貫性の欠落
- ポインタレシーバのメソッド実装で一貫性が失われる場合がある
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値の相互変換を定義
- 構文機能(jsontext)
- v2
- 2層に分かれる
- 構文機能
- jsontex
- トークンベースのローレベル操作
- RFC8259に厳密に準拠した検証
- 位置情報を保持した詳細なエラー報告
- 複雑なjsonでもどこが原因かわかりやすい
- jsontex
- 意味機能
- json/v2
- 構文機能
- ストリーミング方式採用しているので性能向上が実証されている
- 逐次的に処理できる
- オプションを伝搬できる
- 例えば、出力フォーマットにインデントを付与するオプションなど
- 破壊的変更も最小限
- structタグが破壊的
- おまじないなしで、writeやreadを行えるようになった
- 2層に分かれる
- 変わらないもの
- MarshalやUnmarshalの使い心地は概ね同じ
- v2もstructタグの使用方法も継承している
- 新しいもの
- 新しいタグ機能
- カスタム実装の必要性を大幅に減らしている
- 新しいタグ機能
- 移行の段階的ステップ
- ステップ1:v1互換モードでの検証
- v2が提供するv1互換機能を使用して、既存コードに対する互換性を確認
- GOEXPRERIENT=…
- ステップ2:importパスをv2に書き換える
- ステップ3:破壊的変更への対応
- ゼロ値の扱いなど…
- ステップ1:v1互換モードでの検証
- 基本的にv1よりv2の方がパフォーマンスに優れている
- unsafe使用なし