

emacs24では`json.el`というJSONライブラリが標準で付属しています。これはエンコーダとデコーダのみを提供しており、変換後のS式をどのように扱うかはユーザ毎に異なります。

S式の要素を参照するための関数として`car/cdr/assoc/elt`等が用意されています。ただし、これらを多用するとパッと見てどこを参照しているのか分かりにくいように思えます。

[code]

    ;; FILE.json の中身
    ;; {
    ;;   "foo": {
    ;;    "bar": [1, 2, 3]
    ;;   }
    ;; }
    (require 'json)
    (let* ((json-key-type 'string)
           (json (json-read-file "FILE.json")))
      ;; json -> (("foo" ("bar" . [1 2 3])))
      (elt (cdr (assoc "bar" (cdr (assoc "foo" json)))) 0)
    ;=> 1
    
[/code]

そこで、ネストしたS式に対して `(json "foo" "bar" 0)`
のように簡単にアクセスできる書き方（あるいはライブラリ）はあるでしょうか？ライブラリであればMELPA等でパッケージ配布されていると尚良いです。

質問ではElispを対象にしていますが、他のLISP系言語ではS式に変換したJSONやXMLをどのように扱っているのか、参考にできそうな方法を知っていたら教えてほしいです。


CC BY-SA 3.0
2014-12-28T10:07:30.170
2741
2016-06-12T03:00:23.240
2016-06-12T03:00:23.240
754
2391
question
json elisp lisp
S式に変換したJSONデータに簡単にアクセスする方法


以下の様な function を使うのはいかがでしょうか。

[code]

    (defun find-json-value (node json)
      (let* ((key (car node))
             (next (cdr node))
             (rest (and (listp (cdr json))
                        (assoc key json))))
        (if (and key next rest)
            (find-json-value next rest)
          (cdr rest))))
    
    (find-json-value '("foo" "bar") json)
    => [1 2 3]
    
[/code]

単に assoc-list を探索するだけのものですが、ご参考までにどうぞ。


CC BY-SA 3.0
2014-12-28T12:02:42.533
2746
2014-12-28T13:05:12.147
2014-12-28T13:05:12.147
2741
answer


「他のLISP系言語ではS式に変換したJSONやXMLをどのように扱っているのか」について。

SaitoAtsushiさんがGaucheにjsonpathを移植しています。

<http://saito.hatenablog.jp/entry/2013/06/20/233036>

ただ、JSONの緩さのために、XPathのような使い勝手にはならないようですが：

<http://saito.hatenablog.jp/entry/2013/06/25/095332>

XMLについては、Scheme間でポータブルなXPathの実装SXPathがわりと使われていると思います。


CC BY-SA 3.0
2015-01-01T02:42:27.560
2881
2015-01-01T02:42:27.560
5258
2741
answer


「他のLISP系言語ではS式に変換したJSONやXMLをどのように扱っているのか」について、その２。JSONのみですが。

R6RS Scheme処理系であれが、[json-tools](https://github.com/ktakashi/json-
tools)もあります。これは[JSONSelect](http://jsonselect.org/#overview)のScheme版で、CSSセレクタ風のJSONアクセスとSXPathに影響を受けたAPIを提供しています。


CC BY-SA 3.0
2015-02-10T18:26:18.153
6277
2015-02-10T18:26:18.153
5251
2741
answer


`let-alist`というELPAパッケージを見つけたので紹介しておきます。

<http://elpa.gnu.org/packages/let-alist.html>

このパッケージを利用すると、`(cdr (assq KEY ALIST))` という記述の代わりに `.KEY`
のようなドット付きシンボルでアクセス可能になります。シンボルのネストもできます。

ただしパッケージ名の通り、取り扱うデータは連想リスト（なおかつキーがシンボル）のみで配列等にアクセスする機能は提供されてません。

[code]

    (require 'let-alist)
    (let-alist (json-read-file "FILE.json")
      (print .foo)                 ;-> ((bar . [1 2 3]))
      (print .foo.bar)             ;-> [1 2 3]
      (print (elt .foo.bar 0))     ;-> 1
    )
    
[/code]

任意のJSONを扱えるわけではないため、あくまで参考程度です。


CC BY-SA 3.0
2015-09-13T07:04:10.087
16559
2015-11-01T20:00:50.497
2015-11-01T20:00:50.497
2391
2391
2741
answer


MELPA で配布されている Key-Value ライブラリ `kv.el` に連想リストのドットアクセス用関数 `kvdotassoc`
(`dotassoc`), `kvdotassq` (`dotassq`) が用意されています。

<https://github.com/nicferrier/emacs-kv>

連想リストのキーはシンボル `foo.bar` と文字列 `"foo.bar"`
の両方を扱え、キーのネストも可能です。ただし配列要素のアクセスには対応していません。

[code]

    (let ((json (json-read-file "FILE.json")))
      ;; json -> ((foo (bar . [1 2 3])))
      (print (dotassoc 'foo json))          ;-> ((bar . [1 2 3]))
      (print (dotassoc 'foo.bar json))      ;-> [1 2 3]
      (print (dotassoc 'abc.xyz json))      ;-> nil
      )
    
[/code]


CC BY-SA 3.0
2015-10-19T16:47:25.133
17828
2015-10-19T18:28:43.830
2015-10-19T18:28:43.830
2391
2391
2741
answer


dash と dash-functional で次のような形に書くこともできます。 clojure で有名らしい threading macro
らしいです。

[code]

    (->>
     (json-read-from-string "{\"foo\":{\"bar\":[1,2,3]}}")
     (funcall (-compose 'cdr (-partial 'assoc 'foo)))
     (funcall (-compose 'cdr (-partial 'assoc 'bar)))
     (funcall (-flip 'elt) 0)
     )
    
    ;; => 1
    
[/code]


CC BY-SA 3.0
2016-04-28T16:14:26.877
24431
2016-04-28T16:14:26.877
754
2741
answer


ネストした構造をたぐるのは、Clojureの`get-in`やRubyの`dig`のような感じですね。

Clojureの`get-in`のインターフェイスをまねてみると、こんな感じでしょうか。

[code]

    (require 'cl-lib)
    
    (defun get-in (dat keys)
      (cl-reduce
       (lambda (x k) (if (vectorp x) (elt x k) (cdr (assoc k x))))
       keys :initial-value dat ))
    
    (get-in json '("foo" "bar" 0))
    ;;=> 1
    (get-in json ["foo" "bar" 0])
    ;;=> 1
    
[/code]


CC BY-SA 3.0
2016-04-28T17:01:58.400
24433
2016-04-28T17:01:58.400
4010
2741
answer