JSON「最後不能加逗號」是不是錯誤設計?
如何看待json語法中不能加註釋的設計?www.zhihu.com
小小地反對一下 @徐辰 ,JSON結尾允許多餘逗號並不會給 Parser 編寫者帶來多少麻煩。
舉例來說,rapidjson 支持多餘逗號只用了很少的代碼:
至於我個人來說,允許結尾的逗號對於手寫 JSON 時的美觀度和方便度都是有一定提升的(不會因為複製了幾行而忘了刪/忘了加逗號而報錯)比如說 C++ 里寫一個數組的話分多行時這樣就看起來很整齊(單行時結尾不加):
std::vector&
{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0},
{0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 1.0, 1.0}, {0.0, 1.0, 1.0},
// ...
};
如果 JSON 能重新設計的話,我還是希望能允許結尾加逗號的(當然不少解析器也已經實現了這種功能)。
* 當然,如果你認為 JSON 不能用來手寫的話,可以無視這個部分。
JSON 現行標準 RFC7159 [1] 和 ECMA-404 [2] 明確地定義 JSON 的功能是數據互換格式(data interchange format)。作為互換格式,本身就應該盡量簡單,避免一些可有可無的語法。我認為「最後可加逗號」算是可有可無的語法。
雖然如此,我覺得現在還是有一些可有可無的語法。例如字元串里的 (斜線符)可表示為 也可表示為轉義形式 。另外,數字範圍也是個比較大的問題,[1] 里說由實現決定,而又由於有些實現統一用雙精度來表示數字,那麼就不能處理很常見的 64 位整數。
身為 RapidJSON 的作者,希望這個庫能提供最嚴格的 JSON 語法校驗,但同時,不少使用者都想放寬限制,有不少相關討論 [3],我簡單翻譯一些擴展語法需求:
- 根節點可以是任意類型。(JSON 原始標準 [4] 只容許 object 和 array 作為根節點,[1] 已放寬)
- 單行、多行注釋
- 無引號的鍵(那麼是否容許空格、冒號、轉義符?)
- object、array 最後可加逗號
- 單引號字元串(JSON 標準只能用雙引號)
- 小數點為首的數字(JSON 標準中,小數點前必須有最少一個數字)
- 小數點後沒有數字(JSON 標準中,小數點後必須有最少一個數字)
- 十六進位數字(二進位、八進位呢)
- 無窮
- NaN
貧窮限制了我的想像力,甚至,曾經有使用者希望可以支持數字運算表達式??
即使不考慮上述的擴展語法,許多開源的 JSON 解析器/生成器也不能完全符合標準。我在 [5] 測試開源庫性能的同時,也測試了它們是否合符標準(有少部分測試高於標準要求,例如完美還原數字):
因此,個人認為 JSON 標準還是盡量簡單好一些,再複雜、再多可有可無的語法,只會令應用時出現更多問題。所以,我對題目是否定的,這不是錯誤設計,而是一個合理的設計。如果考慮把 JSON 擴展成適合用於人手編寫的格式,或許可考慮 YAML 和 JSON5 等。
更新:看到有答案說輸出 JSON 很麻煩。我只想回應:
不要手工編碼輸出 JSON!
不要手工編碼輸出 JSON!不要手工編碼輸出 JSON!
你知道你用的語言/運行時所輸出的數字必定合符 JSON 要求么?你有處理字元串里的字元轉義么?
就算不使用 DOM,有些庫(如 RapidJSON)還提供最簡單的 SAX 風格 API:
StringBuffer s;
Writer&
writer.StartArray();
for (int i = 1; i &<= 5; i++)
writer.Int(i);
writer.EndArray();
std::cout &<&< s.GetString(); // [1,2,3,4,5]
讓程序庫處理逗號,不要每次都做輪子。
[1] The JavaScript Object Notation (JSON) Data Interchange Format
[2] Standard ECMA-404
[3] Strict/Relaxed JSON syntax · Issue #36 · Tencent/rapidjson
[4] The application/json Media Type for JavaScript Object Notation (JSON)
[5] miloyip/nativejson-benchmark
可能會有人覺得這個題目莫名奇妙,但其實有時候這個末尾逗號有一定的道理。
假設你有一個列表,長這個樣子:
var list = ["a", "b", "c"]
我估計不會有誰去把它寫成:
var list = ["a", "b", "c", ]
這樣很怪,而且沒有必要。但是如果你的 list 長這個樣子:
var list = [
"a",
"b",
"c",
]
我們的 Lint 規則裡面有一條就是當你用這種風格構造數組或枚舉時,要加上最後一個逗號(Multi-line collection literals should have trailing commas)。
這樣的話對版本控制有個好處,就是當你增加一行在尾部的時候,產生的 diff 只有一行,而如果不是這種風格,則會產生三行 diff。
另外一個好處只對某些語言有效果,比如 Python,末尾逗號習慣可以避免產生這樣意想不到的代碼:
list = [
"1",
"2",
"3"
"4"
]
你可能增加一行之後忘記添加逗號,導致你產生了一個:
["1", "2", "34"]
當然這只是一個風格問題,各有優劣。
當然是了,破壞一致性
忘記是哪個語言不支持數組最後一個元素後有逗號,現在加上這個功能了
json 的野雞設計還有很多,所以其實很多情況下都應該自己魔改一下設計
但是考慮到市面上還有縮進的格式和標記……你就應該知道對這個世界不能要求太多從JSON發明初衷來看,或許不是錯誤的設計,但我覺得這是一個糟糕的設計。
我來解釋一下JSON不允許末尾逗號會帶來什麼不方便的地方,舉兩個例子
1. 拼接JSON串
在嵌入式linux環境下(可用的shell命令不多),我要把系統掛載點信息以json格式POST到伺服器上,或許這樣寫代碼最簡單
#!/bin/sh
mount_info() {
echo "["
df | sed 1d | awk "{print $1,$6}" |
while read line
do
printf " {"fs": "%s", "mount": "%s"},
" $line
done
echo "]"
}
curl xxx.xxx/xxx -H "Content-type: application/json" -X POST -d "$(mount_info)"
但是其中有一個問題就是,mount_info生成的JSON會多一個逗號,導致解析失敗。
[
{"fs": "rootfs", "mount": "/"},
{"fs": "/dev/root", "mount": "/rom"},
{"fs": "tmpfs", "mount": "/tmp"},
{"fs": "/dev/mtdblock3", "mount": "/overlay"},
{"fs": "overlayfs:/overlay", "mount": "/"},
{"fs": "tmpfs", "mount": "/dev"},
]
所以我不得不把mount_info的代碼修改為
mount_info() {
printf "["
firstline=true
df | sed 1d | awk "{print $1,$6}" |
while read line
do
if [ $firstline == true ]; then
firstline=false
else
printf ", "
fi
printf "{"fs": "%s", "mount": "%s"}" $line
done
printf "]"
}
2.生成補丁文件
假如我有一個config.json如下
[
"key": "CMXlhcHuN0mCQpitZjN6AudhJEFjimne",
"server": "192.168.1.1"
]
某一個版本開始,我需要指定埠號
[
"key": "CMXlhcHuN0mCQpitZjN6AudhJEFjimne",
"server": "192.168.1.1",
"port": 26817
]
那麼,這個修改將影響兩行,diff出來的差異也並不雅觀
@@ -1,4 +1,5 @@
[
"key": "CMXlhcHuN0mCQpitZjN6AudhJEFjimne",
- "server": "192.168.1.1"
+ "server": "192.168.1.1",
+ "port": 26817
]
我覺得不支持結尾逗號 對開發者很不友好。
經常會因為這個報錯,而且不注意的話 不好找出來。
支持逗號對於寫parser人來說,並不會費額外的很多時間。
所以我覺得是設計錯誤,或者過度設計。
對於程序開發者來說,做測試時手寫json是非常常見的。
JSON 抄襲/借鑒 JS 的時候,JS 對象就是不能有多餘逗號的。
所以其實是 JS 設計問題。這也就是有了json5格式的其中一個原因
是。對手寫不友好,尤其是對版本控制也不友好。每追加一行數據,總是顯示一整行都發生了變化。
風格而已,python好多代碼中函數參數列表可以以逗號結尾,好處是啟用其他參數時非常方便,直接在後面寫就行了,當然這會帶來parse的額外負擔。當然也可以像C那樣把參數寫齊了,不允許參數列表以逗號結尾。JSON也是同樣的道理。
是
是不是錯誤設計就要看這個語言設計時的初衷了。
從人類可讀性的角度討論:
不加逗號,分行的時候不好看;加逗號,單行的時候又不好看。
所以還是 YAML 好,用前置的項目符號來表達數組內的各個元素。
因此 YAML 更易讀。
從計算機可讀性的角度討論:
其實沒差別,trailing commas 還會給 Parser 增加幾行代碼。更何況,JSON 本身就是一種數據傳輸格式,並不是用來存儲下來給人看的,所以對於需要人自己編寫的文件,不管是配置文件也好,數據字典也好,用 XML 或者 YAML 無疑是更好的選擇。
JSON 設計出來是拿來給程序做數據交換的,不是給你人肉寫的
你想要方便人寫的格式,來啊,json5/json5
這個問題叫我想起來,有個語言叫Lua,加不加逗號根據解釋器版本的不同,會產生不同的語義(產生一個nil值或者沒有)
不方便,但是談不上錯誤。
@徐辰 說寫 Parser 麻煩,其實這個麻煩的麻煩程度也就是多寫幾行,頂多十幾行代碼的事,相比其他部分根本算不上什麼麻煩。
@Geoffrey Tang 說 C 之後的很多語言都不支持這種寫法,但是反過來還不是很多語言都支持這種語法?這些語言里常見的有:C#、C++、Python、JavaScript、Rust、Scala……
一群大活人跟死語法較勁?
逗!號前置大法好!
{"a": "a"
,"b": "b"
}
明天 Github Trending:
JSON+, a JSON-like data exchange format but allows comma at the end of objects and arrays.後天 Github Trending:
JSON++, a JSON+-like data format but allows single-quoted keys.大後天 Github Trending:
JSON Plus3, a JSON++-like human-friendly format because it supports comments.......
一個月後:
JSON5, a human-friendly data format that is just fricking awesome cause it-supports-anything-idiots-would-ever-want-to-write-by-their-own-hands.(這玩意真的存在)------
- 你們真厲害哦,我選擇用 XML,可以閉嘴了嗎?
- 那我們搞一個 JSON 和 XML 的融合體吧!(這玩意也真的存在)最後加逗號會不會給人一種……嗯……就是後面還有一個空元素的感覺?
如果JSON最後沒有逗號,以元素結尾,就不會有任何這個意思了。
還是要按照JSON規範去產生。
設不是設計錯誤,也不是parse的問題,完全就是為了規則簡單,就像屬性名必須加引號一樣。
事實上很多解析器也是可以處理不規範格式的。我覺得不是。因為我在給json寫文法的時候發現,最後加不加逗號其實沒什麼太大的區別,寫出來都差不多:
最後逗號可選:[Fuck{"," Fuck}[","]]
最後逗號禁止:[Fuck{"," Fuck}]
最後逗號必須:{Fuck ","}
推薦閱讀:
※我知道英語很重要,也很想學英語,但總是很懶,不想背單詞怎麼辦?
※為什麼大多數編程語言只有異或運算符而沒有同或運算符?
※為什麼棧沒有讀取並彈出的函數?
※Qt 如何打包一個軟體?