すごく単純なJSONパス式

JSONデータの一部にアクセスするためのパス式としては、既に JSONPath (http://goessner.net/articles/JsonPath/) や JPath (http://bluelinecity.com/software/jpath) があります。どちらも、XPathJSON版という位置づけ。これらは便利そうですが、僕の用途にはオーバースペックなので、もっと簡単で小さいな構文を考えてみました。

まず、基本的な構文要素。一部、コメントでお茶を濁してますが、カンベン。


UsualChar ::= {文字列内でエスケープを必要としない印字可能文字}
Escaped ::= {特殊な文字のエスケープ表現}
QuotedString ::= '"' (UsualChar|Escaped)* '"'
NonNegativeInteger ::= '0' | [1-9] [0-9]*
NameStartChar ::= {名前の最初に使える文字、数字は含まれない}
NameChar ::= {名前に使える文字}
Name ::= NameStartChar NameChar*

パス式の構文は次のとおり。


Path ::= '$' | Name
| Path '.' Name
| Path '.' NonNegativeInteger
| Path '[' NonNegativeInteger ']'
| Path '[' QuotedString ']'

説明と注意:

  1. '$' はデータの「ルート」を表す。JSONPath仕様から拝借。
  2. foo.2 という書き方も許す。これは、foo[2] と同じ。
  3. foo["bar"] という書き方も許す。これは、foo.bar と同じ。
  4. プロパティ名が名前でないときは、foo["hello, world."] のような書き方しかできない。
  5. foo . bar とか foo [ 2 ] と書いても問題は起きないのだが、余分な空白は禁止としておく*1
  6. パス式を文字列データとして書くと、"foo[\"bar\"]" のようになる。まー、しょうがないですね。

次の曖昧性に気がつきます。

  • foo["1"] と foo[1] は同じか?

JSONスキーマでは、object型とarray型は排他的(両方に所属するインスタンスはない)と捉えているようです。foo["1"] はオブジェクト(object型のデータ)に対して適用され、foo[1]は配列(array型のデータ)に対して適用されると考えると、次の解釈が妥当でしょう。

  • foo["1"] と foo[1] は同じではない。

実例を挙げましょう。([追記]JavaScriptでは、foo["1"] == foo[1] なので、そのままJavaScriptの式として解釈はできなくなります。[/追記]


{
"foo" : { "0": "apple", "1": "orange"},
"bar" : ["apple", "banana"]
}

上の例に関して:

  1. foo[1] はエラー(構文エラーではなく実行時エラー)
  2. foo.1 はエラー(foo.1 と foo[1] は等価なので)
  3. foo["1"] の値は "orange"
  4. bar[1] の値は "banana"
  5. bar.1 の値は "banana"
  6. bar["1"] はエラー



こんなもんでもけっこう足りるんじゃないのかな。

*1:説得的な必要性があれば、空白を解禁します。