• glassmonkeyさんの発表
  • 元々phperとのこと

v1の課題

動作上の課題

  • 重複キーの受理
    • 最後の値のみ残るのでセキュリティ上の脆弱性
      • 権限の上書きなど
  • 無効UTF-8の許容
    • データ破損リスクを招く
  • 大文字小文字無視
    • v1では区別しないので解釈の不一致を生む要因になる
  • map/sliceのゼロ値
    • jsonを扱う際の直感的な挙動は[](空配列だが、デフォルト挙動のnilが適用されていた
  • 一貫性の欠落
    • ポインタレシーバのメソッド実装で一貫性が失われる場合がある
      APIの扱いにくさ
  • io.Readerからの正確なアンマーシャルが困難
    • json.NewDecoder(r).Decode(v)では入力末尾のゴミデータを拒否できない
  • オプション設定の柔軟性不足
    • EncoderDecoderのオプションがMarshalUnmarshalで使用不可
  • ユーティリティ関数の柔軟性不足
    • CompactIndentHTMLEscape関数は柔軟性に欠ける出力設計
      • 入力は[]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使用なし

資料