JSONのパース・生成 - lisp-cookbook-ja/common-lisp GitHub Wiki
JSONを扱う場合、専用のライブラリを使うと簡単です。
CL-JSON
JSONデータとCommon Lispのオブジェクトを変換するためのライブラリです。動作を細かく変更できるようになっているので、幅広いケースに対応できるでしょう。
;;; JSON文字列の変換
(json:decode-json-from-string "{\"x\": 0, \"y\": 1}")
;=> ((:X . 0) (:Y . 1))
(json:encode-json-to-string '((:x . 0) (:y . 1)))
;=> "{\"x\":0,\"y\":1}"
;;; ストリームからの読み込みと書き出し
(with-input-from-string (s "{\"x\": 0, \"y\": 1}")
(json:decode-json s))
;=> ((:X . 0) (:Y . 1))
(with-output-to-string (s)
(json:encode-json '((:x . 0) (:y . 1)) s))
;=> "{\"x\":0,\"y\":1}"
YASON
CL-JSONと目的やできることが似ていますが、単純な仕組みになっています。JSONデータからCLOSオブジェクトへの直接的な変換はサポートしていません。
JSONデータをパースするにはyason:parseを使います。
(let (l (ht (yason:parse "{\"x\": 0, \"y\": 1}")))
(maphash (lambda (k v) (push (cons k v) l)) ht)
(values ht l))
;=> #<HASH-TABLE :TEST EQUAL size 2/60 #x302000F52B0D>,
; (("y" . 1) ("x" . 0))
yason:parseに入力として指定できるのは文字列とストリームのどちらかです。
(let ((ht (yason:parse "{\"x\": 0, \"y\": 1}")))
(maphash (lambda (k v) (format t "~a, ~a~%" k v)) ht))
;-> x, 0
; y, 1
(with-input-from-string (s "{\"x\": 0, \"y\": 1}")
(let ((ht (yason:parse s)))
(maphash (lambda (k v) (format t "~a, ~a~%" k v)) ht)))
;-> x, 0
; y, 1
JSONオブジェクトは
に変換できます。JSONデータとCommon Lispオブジェクトの対応の詳細はMapping between JSON and CL datatypesを見てください。
;; 標準では比較関数がcl:equalのハッシュテーブルに変換する
(yason:parse "{\"x\": 0, \"y\": 1}")
;=> #<HASH-TABLE :TEST EQUAL size 2/60 #x302000F1E5DD>
;; 連想リストに変換する。:object-asで作るオブジェクトの種類を決められる
(yason:parse "{\"x\": 0, \"y\": 1}" :object-as :alist)
;=> (("y" . 1) ("x" . 0))
;; プロパティリストに変換する。yason:*parse-object-as*スペシャル変数でもコントロールできる
(let ((yason:*parse-object-as* :plist))
(yason:parse "{\"x\": 0, \"y\": 1}"))
;=> ("x" 0 "y" 1)
JSONデータへのエンコードには
を使います。エンコードされた結果はストリームに書き出されます。
;; 基本的にyason:encodeでエンコードする
(let ((ht (make-hash-table :test #'equal)))
(setf (gethash "x" ht) 0)
(setf (gethash "y" ht) 1)
(yason:encode ht *standard-output*))
;-> {"x":0,"y":1}
;; ストリームの指定を省略すると標準出力へ
(let ((ht (make-hash-table :test #'equal)))
(setf (gethash "x" ht) 0)
(setf (gethash "y" ht) 1)
(yason:encode ht))
;-> {"x":0,"y":1}
;; 連想リストからのエンコードにはencode-alistを使う
;; 連想リストのキーはcl:symbol-nameで文字列に変換される
(yason:encode-alist '((:x . 0) (:y . 1)))
;-> {"X":0,"Y":1}
;; JSONオブジェクトのキーを小文字にしたい場合は小文字のシンボルを使う
(yason:encode-alist '((:|x| . 0) (:|y| . 1)))
;-> {"x":0,"y":1}
;; プロパティリストからのエンコードにはencode-plistを使う
(yason:encode-plist '(:x 0 :y 1))
;-> {"X":0,"Y":1}
yason:encodeは総称関数なので、メソッドを定義することでユーザ定義のCLOSオブジェクトを直接エンコードできるようになります。
;; 連想リストを表わすクラスを定義する
(defclass alist () ((content :initarg :content)))
;; エンコードのために特殊化したメソッドを定義する
(defmethod yason:encode ((object alist) &optional (stream *standard-output*))
(yason:encode-alist (slot-value object 'content) stream))
;; オブジェクトを直接変換できるように
(yason:encode (make-instance 'alist :content '((:x . 0) (:y . 1))))
;-> {"X":0,"Y":1}