Wednesday, September 6, 2017

用 EDN 來取代 JSON

我在學習 clojure 的時候,有一個思考一陣子的問題:「程式語言之間的溝通傳輸的格式,要選用 JSON 還是 EDN 。如果使用 EDN 的話,會有什麼明確的好處嗎?」

考慮如下的例子:
要傳遞的資料,有「時間」的資料型態。而中間的傳輸格式,需要使用 rfc 3339
在 EDN 裡,時間可以直接儲存成 rfc 3339 的格式

 #inst "1985-04-12T23:20:50.52Z"

由於 EDN 已經有數種語言的實作可以用,下方是 python3 的範例。
如果傳送的資料格式是用 json 。而且傳遞的資料型態,恰好有一個是 rfc 3339 。 python 的 application logic 就會包含 (1)  json.loads() 和 (2) 一個特定的用來處理 rfc 3339 的邏輯。

與之相對的是,直接用 EDN format ,則是使用 edn_format.loads() 就可以搞定。

相差不多,只差一點點。後者可以讓 application logic 乾淨一點。因為減少了 context dependency 。所謂的 context dependency 就是指為了某些「json 沒有的 data type 」而花力氣去寫的 marshal/unmarshal logic 。

=================================================================

最後,那 EDN 到底跟 clojure 有什麼關系呢? EDN 的延伸資料型態 (extension elements),可以使用標記元素 (tagged element) 去表現。 比方說,rfc3339 就是一種內建的延伸資料型態。 uuid 則是另一種內建資料型態,長成這樣子: 

#uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"

使用者自訂的延伸資料型態,則是長成這個樣子:

#myapp/Person {:first "Fred" :last "Mertz"}

也因為格式的定義如此,每次要對 EDN 增加一種延伸的資料型態時,就是對 EDN 的 parser (每一種語言會有各自的實作) 寫出該種延伸資料型態的 parser plugin ,在 Clojure 的世界也可以稱之為 read macro ,再將這個 parser plugin 註冊到該種資料型態使用的標記元素之後, 這個 EDN parser 就可以無縫地運作。  marshal/unmarshal logic 也就可以完美地放進 parser 裡,不會混淆在 application logic 裡了。