読者です 読者をやめる 読者になる 読者になる

SJSONPath

先日は JSON に対するクエリ言語 JSONPath を Gauche に移植しました。

https://github.com/SaitoAtsushi/Gauche-jsonpath

それにいくらかの改良を加えて sxpath に似た使い勝手になるようにしたので、あらためて解説します。

まず、対象となる JSON は事前に parse-json-string で Schemeデータ形式に変換しておく必要があります。

(use rfc.json)

(define test-object
  (parse-json-string "{\"points\": [
     {\"id\": \"i1\", \"x\":  4, \"y\": -5 },
     {\"id\": \"i2\", \"x\": -2, \"y\":  2, \"z\": 1 },
     {\"id\": \"i3\", \"x\":  8, \"y\":  3 },
     {\"id\": \"i4\", \"x\": -6, \"y\": -1 },
     {\"id\": \"i5\", \"x\":  0, \"y\":  2, \"z\": 1 },
     {\"id\": \"i6\", \"x\":  1, \"y\":  4 }
    ] }"))

そしてクエリを文字列で sjsonpath 手続きへ与えることで手続きが生成されます。

(use sjsonpath)

(define query (sjsonpath "$.points[*].x"))

では生成された手続きを上述のデータに適用してみましょう。

(query test-object)
;; => (4 -2 8 -6 0 1)

指定にあてはまる部分が抜き出されました。

更にクエリ言語にはS式表現も用意しました。 この形式を SJONPath と呼ぶことにしましょう。 クエリを文字列で与える場合と同じ結果が得られます。

(define query2 (sjsonpath '("points" * "x")))

(query2 test-object)
;; => (4 -2 8 -6 0 1)

SJSONPath についてはここでは詳しく述べません。 jsonpath->sjsonpath 手続きによって JSONPath 形式から変換出来るようにしてありますので色々と試してみて下さい。

(jsonpath->sjsonpath "$.points[*].x")
;; => ("points" * "x")

更に興味深い機能として SJSONPath には Scheme の手続きを埋込むことも出来ます。

(define query3 (sjsonpath
                `("points"
                  ,(lambda(node root vars)
                     (and (list? node)
                          (> (+ (* (assoc-ref node "x" 0)
                                   (assoc-ref node "x" 0))
                                (* (assoc-ref node "y")
                                   (assoc-ref node "y")))
                             50)))
                  "id")))

(query3 test-object)
;; => ("i3")

埋込まれた手続きに渡される値は sxpath の仕様とだいたい合わせてあります。

一方で、 sxpath で出来ることが SJSONPath では出来ない点もあります。 S式表現と文字列表現の混在がそのひとつです。 将来的には出来るようにするつもりではあります。

以上、簡単ですが SJSONPath の解説でした。

今までリスト操作で場当たり的にやっていたことが専用のクエリ言語で簡単に記述できるようになってなかなか快適です。

Document ID: 1588455a6d3c786c49a96d1ac09bb6d7