JSON Universe

JSON 在這幾年不但標準化,還開始漸漸的取代了 XML,成為網路上主要的資料交換格式,我認為主要的原因在於他相對於 XML 簡單好懂好用很多,另外一個優點就是他格式很簡單好懂,學起來很快,而相較於更傳統的 Form URL encode 的資料傳輸格式,我覺得最大的優點是他多了 Data Type,而且也可以一口氣送出結構化的資料,傳統的 Form URL encode 雖然有 key value pair,也可以用[]模仿出陣列,不過他的所有的 value 都是字串,在 server 端都還要自己判斷欄位,手動做一次型別轉換。只是雖然 JSON 很好上手,還是有幾個地雷容易踩到,如果是會寫 JavaScript 的人更容易中招,大概列舉一下:

  • 不支援註解
  • 物件的 key 一定要用字串型式,就是一定要用引號框起來
  • 字串一定要用雙引號,不能用單引號
  • 陣列或物件的最後一個元素後面不能加逗點(ES5 允許)

當然要避免這些問題,最保險就是用各個語言已經有人實做好的函示庫來處理,而不要自己用組字串的方式來產生 JSON string。不過這篇文章不是要講這些,其實會想寫這篇文章是從 E4X 那篇文章開始來的,那篇文章有提到 E4X 已經被棄用了,而 Mozilla 建議的替代方案是一個叫 JXON 的東西,名字看起來和 JSON 很像,而事實上除了 JXON,還有一堆名稱和 JSON 很相近的相關技術,像是 JSONH、BSON、LJSON ...等等,不過其實我找不太到有人去收集這些東西,所以就決定自己來整理一下,這篇文章就是要來介紹一下這堆 JSON 衍生出來的東西,以下沒照特定排序。

JXON

第一個要介紹的就是剛剛提到的 JXON,JXON 全名是 lossless JavaScript XML Object Notation,是使用 JavaScript 語言的 syntax 來表示 XML 文件的資料,可以達到完整無失真的呈現,就是說可以還原成一模一樣的 XML,不過其實 JXON 並不是真的有個明確的標準,並沒有一個組織或單位來制訂,雖然如此,還是可以找到有人有實做

JSONx

第二個要介紹的是 JSONx,這是 IBM 推出的一種 XML 文件格式,目的非常有趣,和 JXON 剛好相反,是用 XML 文件來表示 JSON 文件的資料,namespace 是http://www.ibm.com/xmlns/prod/2009/jsonx,講真的我想不到到底有誰會有需求使用到這個標準,隨便看個範例文件吧:

<?xml version="1.0" encoding="UTF-8"?>
<json:object xsi:schemaLocation="http://www.datapower.com/schemas/json jsonx.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx">
  <json:string name="name">John Smith</json:string>
  <json:object name="address">
    <json:string name="streetAddress">21 2nd Street</json:string>
    <json:string name="city">New York</json:string>
    <json:string name="state">NY</json:string>
    <json:number name="postalCode">10021</json:number>
  </json:object>
  <json:array name="phoneNumbers">
    <json:string>212 555-1111</json:string>
    <json:string>212 555-2222</json:string>
  </json:array>
  <json:null name="additionalInfo" />
  <json:boolean name="remote">false</json:boolean>
  <json:number name="height">62.4</json:number>
  <json:string name="ficoScore"> > 640</json:string>
</json:object>

MessagePack

MessagePack 是一個類似 Protocol Buffer 的 Binary 資料交換格式,不過其資料的構成很 JSON,官網上的測試頁面就可以讓你直接輸入 JSON 資料,然後它會轉成 MessagePack 的格式。

MessagePack Try

MessagePack 相較於下面介紹的其他幾個 Binary 格式是比較好懂些,也有比較完整的實做和支援,是目前比較多人使用的 binary JSON 格式,也有一些商業網站採用,像是 Pinterest、fluentd。這類的 binary 資料格式蠻常被拿來和 Google 的 Protocol Buffer 來做比較,當然相較於 Protocol Buffer 來說,JSON 型式的資料結構其特色就是 schema free,不用事先定義好傳輸資料內部的結構。

BSON, BJSON

BSONBJSON 也都是 Binary 的 JSON 表現格式,不過這兩個規格都不完全相容於 JSON,有多一些自定義的資料型態,像是 regular expression,然後大部分人都只有談到 BSON,不太有人講到 BJSON 的樣子。

UBJSON, Smile

UBJSON(Universal Binary JSON) 和 Smile 也和上面幾個一樣是 Binary 的資料格式,和 BSON 比起來則是差在這兩個格式完全相容於 JSON,意思就是可以做到UBJSON -> JSON -> UBJSON這樣的資料轉而完全不會遺失資訊。

Binanry 的 JSON 表現格式其實還有一個叫 xJSON 的比較沒知名度這樣。

Hjson

Hjson 全名是 the Human JSON,顧名思義就是更人性化的 JSON 格式,是一個比 JSON 寬鬆的規範,例如允許註解,不用,改成用換行也可以分開不同元素:

[
  1
  2
  3
]

物件的 key 不需要字串的引號:

{ foo: 123 }

等等很多方便的語法,不過其實如果要用機器產生這些寬鬆的資料是不太好,比較適合的場合還是用人工維護的 JSON 檔案。

rson

rson 是一個 JSON 的 superset,和 Hjson 的目標有些接近,不過 rson 的一些新語法更接近 YAML 格式,專案是放在 Google Code 上,而且似乎已經沒維護了,是個總有一天會消失在網路上的東西吧。

LJSON

LJSON 的 L 應該是 lambda 的意思,也是個 JSON 的擴充格式,改變只有一個,就是多了可以儲存 pure function 的能力,儲存 function 的形式是很像 ES6 arrow function 的樣子:

(v0)=>({"author":"John","message":v0})

最大的差別在於=>後面是接(),並且還有個特色是它會想辦法最佳化,會有像是 dead code removal 的效果。如果要自己做到用 JSON 儲存 function 的話,一般是可以先用toString來輸出 function 的原始碼,要還原時再用new Function來還原,不過如果不是 pure function,例如有用到 closure 變數的話,就一定會失去這些 reference 到的變數了。

另外還有一個 LJSON 是叫做 Loose JSON,就和上面介紹的 Hjson 目標差不多,就不再多介紹了。

JSON Lines

JSON Lines 也是有點接近上面介紹的幾個東西,其實是個很簡單的東西,就是把多個 JSON 資料用換行接在同一個文件檔案內,例如:

[1,2,3]
[4,5,6]
[7,8,9]

三個 JSON 陣列接在一起,不是一個合法的 JSON string,一般 JSON Parser 都會說 Syntax Error,不過支援 JSON Lines 的環境下就可以把這串字串切開,分成三個陣列,支援的環境中我看到比較有名的大概就是 dat 了,這是一個蠻厲害的資料分享的工具,其中的串流模式傳輸資料就是用到 JSON Lines 格式(其實就是一個一個 JSON 資料輪流輸出)。

JSONH

JSONH 就不是一個 JSON 的擴充格式了,它事實上是用來壓縮 JSON 的,特別適用於物件的陣列的資料集,像是:

[
  {
    "name": "John",
    "gender": "Male",
    "country": "USA"
  },
  {
    "name": "Smith",
    "gender": "Male",
    "country": "Canada"
  }
]

透過用 JSONH 壓縮就會變成:

[3, "name", "gender", "country", "John", "Male", "USA", "Smith", "Male", "Canada"]

最主要節省的就是大量重複的屬性名稱,如果屬性名稱長,陣列又長,這樣壓縮前後的資料量大小差距就會很明顯,不過如果資料階層比較複雜的話,沒辦法整理得這麼漂亮,壓縮效果就會打折了。

Jsonnet

Jsonnet 是 Google 所推出,帶著一點程式語言特性的 JSON 文件格式,例如多了個self

{
    person1: {
        name: "Alice",
        welcome: "Hello " + self.name + "!",
    },
    person2: self.person1 { name: "Bob" },
}

還有 tempalte string:

{
    fmt2: "The word %(wd)s has %(le)d letters.  Go %(wd)s!"
        % {wd: $.word, le: std.length($.word)},
}

Array comprehension:

{
    foo: [1, 2, 3],
    bar: [x * x for x in self.foo if x >= 2],
    baz: { ["field" + x]: x for x in self.foo },
    obj: { ["foo" + "bar"]: 3 },
}

除了這些程式語法的特性之外, Jsonnet 還有像是 Hjson 的一些比較人性化的語法的支援,官網上則自稱是是一種 data template language,而不是單純的資料而已。

JSON3

JSON3 其實不是一個新版本的 JSON,而是一個更加穩健的 JavaScript 的 JSON 實做,多處理了一些不同瀏覽器的差異問題,3 的由來其實是相對於 Douglas 的 json2.js,這個 json2.js 是整個 JSON 進 ECMAScript 標準時最主要的依據,包括 Global Varibale 的名稱,API 的設計都是從這邊來的,至於這個 json2.js 的 2 的意思其實就是這是 Douglas 實做的第二版 JSON library。

JSONP

JSONP 其實不是一個標準,最早出處也不詳,不過早一點在 CORS 普及前就開始寫 Ajax 的人應該是很熟悉的東西,瀏覽器為了安全性問題,雖然可以跨直接用 XHR 做出 HTTP request,但是會限制跨 domain 的連線,現在有 CORS 可以讓網站所有者提供允許的白名單,但是在 CORS 普及前,基本上是完全被禁止的,不過有個方法就是插入<script>標籤去執行遠端的 js 檔案,這個方法的缺點就是只能執行,不能讀取檔案內容,JSONP 就是在這個限制下所設計出來可以用來跟遠端要資料的方法,基本上實做的方法就是在本地先產生好一個暫時的 callback function:

window.cb123456 = function (data) {
  window.cb123456 = null; //clean up

  //do something with data      
};

然後把遠端資源加上參數指定 callback 名稱:

<script type="text/javascript"
     src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=cb123456">
</script>

遠端收到 Request 後產生特別的 response,本來要回傳的資料如下:

{
  "name": "John",
  "gender": "Male",
  "country": "USA"
}

現在會變成一段 JavaScript 程式碼:

cb1231465({
  "name": "John",
  "gender": "Male",
  "country": "USA"
});

JSON Schema

JSON 本身是很自由的資料格式,不過開發比較大型的應用程式,或是要做一些測試的時候,太過自由就不一定是優點了,所以很合理的就會有個來定義資料內容結構的描述語言出現,JSON Schema 就是一個,而且似乎很合理的 JSON Schema 也是 JSON 文件:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "id": {
            "description": "The unique identifier for a product",
            "type": "integer"
        }
    },
    "required": ["id"]
}

這個文件格式也有在 RAML 中被使用到,RAML 是個 RESTful API 的定義文件,如果 API 傳輸的資料是 JSON 的話,就可以內嵌 JSON Schema,都定義完整就可以用自動化工具來做一些測試了,或是也可以產生一些方便閱讀的文件等等。

JSON-LD

JSON-LD 是 JSON for Linked Data,所謂 Linked Data 其實就是語意網的一部分,基本上還是遵照 RDF 架構去設計出來的文件,目前也很多網路服務都支援,像是 Google、Facebook 等等,甚至 Gmail 的一些特殊能力也是透過在信件內插入 JSON-LD 格式的資料才得以實現。

JSON-RPC

JSON-RPC 基本上就是 XML-RPC 的 JSON 版本這樣,其實我也不知道有沒有人真的拿來應用就是,倒是 XML-RPC 現在還不少老東西有支援。

GeoJSON

GeoJSON 則是針對地理資訊的 JSON 資料格式,基本上就是點線面,然後座標用經緯度,再加上 meta 資訊這樣,不會很複雜,GitHub 也支援 直接預覽 的功能,g0v 上其實有不少專案都有用到 geojson 資料,還有個 twgeojson 的基礎建設,最新版的檔案有點大,不過可以看看舊版本的檔案

twgeojson g0v,

GeoJSON 還有一個擴充版的文件格式叫做 TopoJSON,相較於 GeoJSON 比較適合用在 topology 上(可以想像你只要台灣地圖,但是不用放在世界地圖上),整體檔案大小也因此可以小很多。

JSON-stat

JSON-stat 是個把二維統計資訊表格用 JSON 表現的格式,而且不是只能夠處理兩個維度的資料而已,事實上可以儲存更多維度,JSON-stat 在儲存 raw data 的部分是用打平的一維陣列來儲存,所以也不用擔心太多層太過複雜的狀況,想詳細瞭解一點的可以參考官網右上角的投影片看看。

JSON Graph

JSON Graph 是 Netflix 推出的 Falcor 裡面用到的,Falcor 是一種用來取代 RESTful API 的新的前後端之間的溝通方式,這種方法和 RESTful 相比最大的差異就是不再是對單一的資源(resource)做操作,而是整個應用程式會用到的東西都組織定義好,放到一個大的 resource 內,然後再對這個大的資源做操作,而這個大的 resource 就是所謂的 JSON Graph 物件,Falcor 的出現是為了解決一些把 RESTful 用到很透徹時會出現的問題,不過真的要想用這套方法在自己的服務上其實是有些難度,因為要實做一個 Falcor Server,官方也只有提供 Node 版的。

JSONPath

JSONPath 是一個 JSON 的 query 語言,像是 XPath 之於 XML,語法 syntax 其實和普通 JavaScript 程式碼很像,不會太難理解,一些範例:

$.store.*
$.store.book[*].author
$.store..price

不過這些可不是程式碼,而是單純的字串,丟進 JSONPath 引擎再給它相對應的物件(不是 JSON 字串),就可以把目標位置的資料取出來。

JSONiq

JSONiq 也是個用來 query JSON 資料用的語言,不過和上面的 JSONPath 不一樣,比較像是 SQL 那樣有點程式化,而且語法偏 functional programming,例如:

for $sarah in collection("users"),
    $friend in collection("users")
where $sarah.name eq "Sarah"
      and
      (some $name in $sarah.friends[]
       satisfies $friend.name eq $name)
return $friend

JSON Pointer

JSON Pointer 是一個 2013 的 RFC 標準(RFC-6901),用途和 JSON Path 有點像,也是用來取出 JSON 文件中的一部份資料,不過最主要的差異在於 JSON Pointer 一定是只能指向到一個節點,只能拿到一個 value,不像 JSONPath 或是 JSONip 可以一次 query 很多節點,然後用陣列的形式傳回值;另外還有一個和 JSONPath 的差異是 JSON Pointer 和 XPath 的語法很接近:

/foo/0
/actors/4/name

JSON Patch

JSON Patch 是一個另 RFC 標準(RFC-6902),緊接在 JSON Pointer 之後,這兩個標準都是都是在 gslin 那邊看到的,JSON Patch 主要是定義了一組標準的對 JSON 的操作(operation),提供了 add, remove, replace, move, copy, test 幾個操作,基本上看起來如下:

{"op": "add", "path": "/pets/0", "value": "dog"}

這行的意思是要在 pets 下的第一個元素的位置加上一個值,字串 "dog",JSON Patch 基本上是設計來和 HTTP Patch 配合的,以前如果要用 Patch 修改某個特定網路 reource 的話,一定要把整個完整的,更新過後的 JSON 資料傳一次,但是如果用 JSON Patch 就可以只傳要做怎樣的更新就可以了,可以節省很多資料的傳輸量,而且還可以先用 test 做測試,然後也可以一次做一連串的修改動作:

[
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo"}
]

JSONDiffPatch

JSONDiffPatch 是本來文章發出後才看到的 JSON 文件的 diff, patch 工具,網站上的 demo 還有視覺化的顯示,感覺很華麗:

JSONDiffPatch,

雖然用的格式不是 JSON Patch,而是自己有定義他的 delta format,不過基本上作的事情很接近,另外他還可以 undo patch,也有 plugin 可以外掛,還算蠻完整的。

JSONLint

JSONLint 是 JSON 的文件格式檢查工具,不過實際上大部分人在命令列用的通常是 npm 裝的版本,這版是 Mozilla 的 Zach Carter 用 JavaScript 實做的,我自己還蠻常用一個線上服務:JSON Formatter,除了可以做語法檢查,還有可以收合展開的樹狀檢視模式。

?JSON

最後,JSON 的相關應用和各種 library 真的是不少,其實 JSON 前面加上 26 個英文字母的任意一個,只有ojson沒有東西而已,其他 25 個名稱都有人用,不過大部分都是特定語言的 JSON library 實做就是了。然後相信我這篇文章列出來的不是全部,如果有人知道什麼特別的變化或應用也歡迎提供~

PS: 還有一篇 JSON Web Token