JSON の設計をする

JSON を作る際の基本と気を付けるべきことをまとめてみました。

JSON と JavaScript の違い

JSON は JavaScript の記法を元に作られているので、JSON を JavaScript として実行することが可能です。しかし、 JSON はいくつか追加のルールがあります。以下が代表的なものです。

  • キーは ” で囲む
  • 最後の要素は , を付けてはいけない
  • Unicode エンコード必須(日本語→\u65e5\u672c\u8a9e と書かなければいけない)
  • コメント書けない

正直 Unicode エンコードは守ってる人少ないです(日本語ユーザ的には不便すぎるので)1完全に Java property file と同じ轍を踏んでいる。守らなくても処理できるっていうのもあります。JavaScript の JSON.parse に通れば大体大丈夫です。

上記仕様から、人間が読み書きすることはあまり考慮されておらずあくまで機械同士の通信で使うものという印象を受けます。人間が書くもの、例えば設定ファイルなどは JSON ではなく YAML を採用した方が分かりやすいでしょう。

JSON の設計ツール

最近、REST API は Open API で設計するのが当たり前という風潮になりつつあります。今回は API ではなく JSON の作成についてなので、 Open API の中で使われている JSON Schema という部分が重要になります。Open API のリクエスト、レスポンスで JSON を使う場合に使用されるもので、JSON の構造を JSON (Open API の場合は YAML でも) で記述できます。

ただ、 JSON Schema 単体のツールよりも Open API 関連のツールの方が充実しているので、 Open API のツールを使って JSON Schema を書く方が簡単です(Open API を書いてから JSON Schema 部分を手動で抜き出すという荒業ですが・・・)。Open API を書けるツールとしては以下のようなものがあります。

  • Swagger
  • Postman
  • Stoplight
Swagger Editor より

ただし、 Open API は JSON Schema の仕様を拡張しているので注意が必要です(例えば integer 型や nullable は JSON Schema ではなく Open API の仕様)。

JSON Schema がなくても JSON は作れるのですが、こういったツールを利用した方が正しいものを作れるのでおすすめです。

基本の型

JSON の仕様(RFC8259) JSON Schema にある通り、以下の基本型が存在します。

  • null
  • boolean (true/ false)
  • object
  • array
  • number
  • string

少なく感じますが、このシンプルさが JSON の魅力です。

数値の取り扱い

JSON の number は精度についての制限はありません。実装側(JSON を処理する側)で任意の精度を決めていいようです。ただ、RFCでは補足として double (倍精度の浮動小数点数)が広く普及しているのでそれに合わせると互換性がよくなるでしょうというような内容が書かれています。

要するに高い精度が要求される数値は number 型を使用してよいか検討が必要という事です。例えば以下のような数値です。

  • 金額
  • 大きな整数

金額については double でも何とかなる場合があります。計算をする場合は double だと誤差が生じやすい(0.1 + 0.2 の結果など)ですが、値を受け渡すだけであれば誤差は生じない場合が多いです(参考)。ただ、安全をとりたいならば number ではなく string を使うほうがいいかもしれません。

大きな数については昔 Twitter の API で話題になりました。ID の数値が大きくて受け取る側の実装によってうまく扱えないという問題です。その際は number と string 型それぞれの項目を用意することで問題を回避したようです。

オブジェクトの混合

まれに見かけるアンチパターンなのですが、データ構造で言うマップ(辞書)、配列が混合してしまっている場合があります。これは実装によってうまく処理できないので避けましょう。

{
    "id": 1,
    "name": "なまえ",
    "attr_color": "属性1",
    "attr_size": "属性2",
    "0": "項目1",
    "1": "項目2"
}

上記の例では id, name はいいのですが、 attr_xx という個数やキー名が可変と思われるの項目や、 “0” “1” のように配列のインデックスのような項目が混在してしまっています。

この JSON を受け取った側は attr と 配列のようなものの項目を簡単に取得できないので、 0 はあるか、 1 はあるか・・・、すべての項目を検査して attr_ から始まるキーはあるか、というように順に探索する処理を書く必要がありかなり手間です。ライブラリやオブジェクトマッパーで処理できずに自分で処理を書くなど、かなり悲惨なことになります。

{
    "id": 1,
    "name": "なまえ",
    "attr" : {
        "color": "属性1",
        "size": "属性2"
    },
    items: [
        "項目1",
        "項目2"
    ]
}

マップはマップとして、配列は配列として1つ項目を設けてあげましょう。

JSON と似たもの

JSON を拡張した仕様として、以下のようなものがあります。

  • jsonc (JSON with Comments)
  • jsonl (JSON Lines), NDJSON

jsonc は JSON にコメント /* */ を書けるようにしたものです。 VS Code の設定ファイルなどで使われます。

jsonl や NDJSON は json を改行区切りで複数書けるものです。ログファイルや bulk 処理系・ストリーミング系の API (ElasticSearch など) で使われます。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください