JSON (JavaScript Object Notation) 是一種輕量級的資料交換語言。隨著 web 變得越來越重要以及 node.js 的興起,JSON 已經變成現在非常重要的資料格 式了。
JSON 格式到底有什麼好的呢?這要從 XML 開始說起了,在 XML 當中對於資 料會寫成以下的形式
<person first-name="John" last-name="Smith"/>
或是
<person> <first-name>John</first-name> <last-name>Smith</last-name> </person>
你也可以寫成這樣
<object type="Person"> <property name="first-name">John</property> <property name="last-name">Smith</property> </object>
當然你也可以寫成其他你喜歡的 XML 格式。如果我們將這樣的資料轉換成 JSON 格式的話,則會變成這樣
{ "first-name" : "John", "last-name" : "Smith" }
有沒有注意到原本很複雜的資料格式,突然變成清爽多了?
若你有興趣想更加理解 JSON 格式,可以參考: 你不可不知的 JSON 基本介紹
使用 json.el
emacs 已經內建 json.el 函式庫,你只需要將他載入就可以用相關的函式了
(require 'json)
JSON 的原始數據類型 (primitive values) 是字串、數值、關鍵字 true、 false 以及 null。
(json-read-from-string "true") ; => t (json-encode t) ; => "true" (json-read-from-string "4.5") ; => 4.5 (json-read-from-string "\"foo\"") ; => "foo"
JSON 的複合類型則是陣列 (array) 與字典 (dictionary)
(json-read-from-string "[true, 4.5]") ; => [t 4.5] (json-read-from-string "{\"foo\": true}") ; => ((foo . t))
注意到我們從 JSON 陣列讀回的格式是向量 (Vector),而從 JSON 字典讀回來的 格式則是關聯串列 (alist, Association Lists)。
我們可以用 json.el 自由修改我們想讀回來的資料型態,比如說
從 dictionary 讀回 plist (Property Lists)
(let ((json-object-type 'plist)) (json-read-from-string "{\"foo\": true}")) ; => (:foo t)
使用 key 讀回字串 (string)
(let ((json-key-type 'string)) (json-read-from-string "{\"foo\": true}")) ; => (("foo" . t))
我們也可以將 Lisp 數據透過 json.el 轉成 JSON 數據格式
(json-encode '(1 2 3)) ; => "[1, 2, 3]" (json-encode '(:foo 1 :bar 2 :baz 3)) ; => "{\"foo\":1, \"bar\":2, \"baz\":3}"
In Lisp, code is data
看完上面將 JSON 轉換成 emacs lisp 的介紹,讓我們看看 Lisp 下面是否需要這 樣的數據格式,答案是不需要。
對 Lisp 語言而言,程式碼就是數據 ,舉例來說,我們有以下的程式碼
(message "hello emacs")
將他當作數據使用的話,只要使用 ' 或是用 quote 包住即可
'(message "hello emacs") ;; or (quote (message "hello emacs"))
我們可以用 eval 來將 lisp 數據變回程式碼
(eval '(message "hello emacs")) ; => hello emacs
以這樣的規則來看,前面所說的 JSON 轉換成 Lisp 差不多式如下的形式
JSON
{ "first-name" : "John", "last-name" : "Smith" }
Lisp
( :first-name "John" :last-name "Smith" )
根據你用的 Lisp 方言類型不同,你可以轉換成的形式也不同,像在本範例中, 你可以用 emacs lisp 的 cons cell 來替代 JSON 資料
(("first-name" . "John") ("last-name" . "Smith"))
比如我們要取得 John,則可以這樣
(let ((data '(("first-name" . "John") ("last-name" . "Smith")))) (cdr (assoc '"first-name" data))) ; => "John"
後記
即使 javascript、JSON 再怎樣盛行,在這當中我只看到了一個又一個 Lisp 的 克隆 (唯一成功的地方是語法,讓初學者看到會想學,但我覺得 Lisp 去掉 S-exp 就不再是 Lisp 了),從這篇文章 裡使用 json.el 將 JSON 資料轉換成 Lisp 資料更可以體會何謂 In Lisp, code is data ,對於 Lisp 而言, 是不需要這種奇怪的資料格式的,這也是我如此喜歡 Lisp 的理由之一。