非エンジニアに贈る「具体例でさらっと学ぶJSON」

aws_icon-awscli

よく訓練されたアップル信者、都元です。「JSONってなんとなくわかるけど、構造を読み取れる自信はないし、ましてや書ける気はまったくしない。」そんな人に贈るエントリーです。残念ながら、「13日の金曜日」や「Why Japanese people!!」しか思い浮かばない人は、想定読者対象外ですのでお引き取りください。逆に、正確な定義が欲しい人はRFC 4627をご確認ください。

「JSON」とは。

JSONとは JavaScript Object Notation の略で、要するに「JavaScriptの中でオブジェクトを記述する書式」のことです。と言われてもなんのことだか分からない人向けの説明なので安心してください。

以下、ざっくりとボトムアップ(細かいことを先に理解して、最後に全体を理解)で説明します。

「値」とは

まず本稿における「値」という言葉を確認しておきましょう。難しく言えば「JavaScriptにおいて、代入文の右辺に記述できる、リテラルのみで構成された記述」としたいんですが、JavaScriptなんて知らない人向けに説明するために、具体例で説明します。

JSONの世界には下記6種類の値が存在します。下記に示した記述は全て「値」です。ちなみにリストの上4つを「単純値」、リストの下2つを「複合値」と呼ぶことにします。(単純値と複合値という言葉は本稿での造語なので注意)

  • 文字列値(string):"abc""def"等、ダブルクオートで囲んだ任意の記述。
  • 数値(number):123.45等、数値を生書きした記述。
  • 真偽値(boolean):true及びfalseという「yes/no的な意味合い」を表現する特別な記述。
  • ヌル値(null):nullという「値がカラッポであること」を表現する特別な記述。
  • 配列値(array):[][1][1,true]等、複数の値の並びを表現する記述。
  • オブジェクト値(object):{}{"foo":"bar"}{"foo":"bar", "baz":null}。これは説明が複雑なので後回し。

一方、下記は「値」ではありません。

  • abcd"ef""ab(全体がクオートされていなかったり閉じていない。)
  • TRUENULL(大文字と小文字は区別する。)
  • [1][2,3][1],[2,3]{}{}(複数の値がつながったものは値ではない。)

一般的に「値」というと多くの人は単純値の方だけを頭にうかべます。ここでは、「今まで漠然と値だと思ってたものは単純値と呼ぶんだね」、「値っていうものを厳密に考えると、(理解しづらいけど)複合値ってのも含むんだね」ってことを受け入れてくれれば大丈夫です。

「メンバー」とは

次に「メンバー」という言葉です。これは、「文字列値とコロン(:)と値を繋いだもの」とします。下記は全て「メンバー」です。

  • "foo": "hoge"
  • "bar":"fuga"
  • "baz":1
  • "qux":[]

一方、下記は「メンバー」ではありません。

  • 1:2(左辺が文字列値じゃない。)
  • "foo";"bar""foo"1(コロンがない。)
  • "foo":"bar":"baz"(コロンが2つ以上ある。)
  • "foo":hoge(右辺が値じゃない。)

値であるものはメンバーではなく、メンバーであるものは値じゃない、排他的な関係にありますね。

配列値の書式詳細

さて、上では少々さらっと流してしまった配列値について。

配列値というのは[ ... ]という形式で、"..."部分にはカンマ区切りの「値」を0個以上含むものです。それぞれの値のことを「要素」と呼びます。それぞれの要素の種類は一致している必要はありません。値ならなんでもいいので、配列の中に配列値が入っていても、配列の中にオブジェクト値が入っていても構いません。配列値の例は下記の通りです。

  • []
  • [1]
  • [true]
  • ["foo", 23, null]
  • [1, {}, true, [[], 2, [3,4]]]

そして、配列値ではないものの例は下記。

  • 1, 2, 3{1, 2, 3}("[]"で囲まれていない)
  • [foo, 1](中身に値じゃないものが含まれてる)
  • ["foo" 23 null](カンマ区切りじゃない)

配列値は複数の要素から構成されていることがわかりました。そして、配列に対しては、「インデックス」と呼ばれる番号で、要素を取り出すことができます。例えば上で例に挙げた["foo", 23, null]ですが、「この配列のインデックス1の値は何?」という問に対しては23と答えられます。(最初の要素がインデックス0、2番目の要素がインデックス1です。0-basedなので1つズレます。)

配列は「中身の順序が大事」なのです。

練習問題1:配列[1, {}, true, [[], 2, [3,4]]]の、インデックス3の値は何ですか?(回答は本稿末尾)

オブジェクト値の書式詳細

そして、上で後回しにしたオブジェクト値について。

オブジェクト値というのは{ ... }という形式で、"..."部分には、カンマ区切りの「メンバー」を0個以上含むものです。

  • {}
  • {"foo": "bar"}
  • {"foo": "bar", "baz": "qux"}
  • {"foo": "bar", "baz": {"foo": "bar", "baz": "qux"}}
  • {"foo": [1, null], "baz": {"foo": [true, "bar"], "baz": "qux"}}

そしてオブジェクト値ではないものの例がこちら。

  • "foo": "bar", "baz": "qux"["foo": "bar", "baz": "qux"]("{}"で囲まれていない)
  • {"foo": bar, "baz": "qux"}(中身にメンバーじゃないものが含まれている)
  • {"foo": "bar" "baz": "qux"}(カンマ区切りじゃない)

上で1セクション使って「メンバー」の構造を説明しましたが、メンバーというのはオブジェクト値の構成要素として登場するだけで、出番はここだけです。オブジェクト値の書き方を理解してしまえば、メンバーについては名前を忘れてしまっても構いません。

さて、オブジェクト値は複数の要素から構成されていることがわかりました。そして、オブジェクトに対しては「キー」と呼ばれる文字列で、要素を取り出すことができます。例えば上で例に挙げた{"foo": "bar", "baz": "qux"}ですが、「このオブジェクトのキー"baz"に対応する値は何?」という問に対しては"qux"と答えられます。

一方、「このオブジェクトのインデックス1(つまり2番目)の要素の値は何?」という問に対しては答えられません。下記2つのオブジェクトは同じデータを表現している(等価)ので、どちらが2番目かは定めることができないからです。

  • {"foo": "bar", "baz": "qux"}
  • {"baz": "qux", "foo": "bar"}

オブジェクトは「中身に順序の概念は存在しない」のです。

練習問題2:オブジェクト{"foo": [1, null], "baz": {"foo": [true, "bar"], "baz": "qux"}}の、キー"baz"の値の、さらにキー"foo"の値の、さらにインデックス0の値は何ですか?(回答は本稿末尾)

「JSON-text」とは

さて、最終的に。「JSON-text」とは何でしょうか。複合値を表現するテキストのことです。単純値を表現するものはJSON-textではありません。そしてそもそも「値」でないものは全て、JSON-textではありません。

インデントと整形

ここまであまり触れませんでしたが、JSON-textの中では(1つの単純値を分断しない限り)スペースと改行を自由に挿入して構いません。上に例で挙げた

{"foo": [1, null], "baz": {"foo": [true, "bar"], "baz": "qux"}}

は読みづらいですが、下記のように人間に分かりやすいように改行とスペースを挿入してやると、配列の要素の関係や、メンバーを認識しやすくなります。

{
  "foo": [ 1, null ],
  "baz": {
    "foo": [ true, "bar" ],
    "baz": "qux"
  }
}

どちらも同じデータを表しており、どちらもJSON-textです。

なぜJSONを使うのか

世の中には様々な形を持ったデータがあります。あらゆるデータがExcelの表形式でわかりやすく表現できればいいのですが、そうもいきません。例えば、10個のEC2インスタンスがあったとして、それぞれについて、下記の情報を表現したいとします。

  • インスタンスID
  • 起動日時
  • AMIのID
  • ...

この辺りまでは良いでしょう。表形式で表現できますね。まだ続きがあります。

  • ...
  • EIP情報(1つとは限らない)
    • 1つ目のEIP
    • 2つ目のEIP
    • ...
  • タグ情報(もちろん1つとは限らない)...

こんな感じのデータになってくると、もう表形式が破綻してしまいます。表よりは読みづらいのは確かですが、こういった情報を表現できるのがJSONです。

[
  {
    "InstanceId": "i-XXXXXXXX",
    "ImageId": "ami-YYYYYYYY",
    "LaunchTime": "2015-05-28T08:30:10.000Z",
    "Tags": [
      {
        "Value": "portnoydev-emr",
        "Key": "Name"
      },
      {
        "Value": "j-ZZZZZZZZZZZZ",
        "Key": "aws:elasticmapreduce:job-flow-id"
      },
      {
        "Value": "CORE",
        "Key": "aws:elasticmapreduce:instance-group-role"
      }
    ]
  },
(略)
]

一回絶望するといい

最後に、下記もJSONの一種なんですよ。っていう現実を見て一回絶望してください。

練習問題の回答

{
  "answers": {
    "練習問題1": "[[], 2, [3,4]]",
    "練習問題2": "true"
  }
}