07.30 為什麼 JSON 不適合作為配置語言?

為什麼 JSON 不適合作為配置語言?

為什麼 JSON 不適合作為配置語言?

許多項目用 JSON 作為配置文件。也許最著名的例子就是 npm 和 yarn 使用的 package.json,還有許多其他項目也用 JSON,如 CloudFormation(最初僅支持 JSON,現在還支持 YAML)和 composer(PHP)。

但是,有幾個原因表明 JSON 其實非常不適合作為配置語言。不要誤會,我很喜歡 JSON。它非常靈活,而且機器和人類讀起來都很容易,是個非常好的數據交換和存儲的格式。但作為配置語言,它有許多缺點。

為什麼 JSON 不適合作為配置語言?

為什麼 JSON 成了流行的配置語言?

JSON 被用於配置文件有好幾個原因。最大的原因就是很容易實現。許多語言的標準庫都支持 JSON,標準庫不支持 JSON 的語言也會有非常易用的 JSON 包。其次就是開發者和用戶都已經很熟悉 JSON 了,不需要再為產品學一種新的配置格式。更不用說 JSON 的各種相關工具,包括語法高亮、自動格式化、驗證工具等等。

這些都是很好的理由。可惜這種格式真的不適合作為配置文件。

為什麼 JSON 不適合作為配置語言?

JSON 的問題

沒有註釋

配置語言的一個絕對必要的功能就是註釋。註釋非常有用,它能標識出配置項的可能的值,以及選擇某個值的原因,還有最重要的一點就是臨時註釋掉某部分配置以方便測試和調試。而作為數據交換格式的 JSON 完全沒有使用註釋的必要。

當然也有方法解決這個問題。最常見的方法就是在對象中使用特殊的鍵作為註釋,如"//"或"__comment"。但是,這種語法並不是很易讀,而且如果要想在同一個對象中使用多個註釋,就得給每個註釋使用唯一的鍵。David Crockford(JSON的發明人)建議使用預處理器去掉註釋(https://plus.google.com/+DouglasCrockfordEsq/posts/RK8qyGVaGSr)。如果你的應用程序要求 JSON 作為配置文件,那麼我建議使用這種方法,特別是如果有某種構建過程的話。當然,這會給編輯配置帶來一些額外的工作,因此如果你正在創建的應用程序需要解析配置文件的話,不要依賴用戶去做這項工作。

一些 JSON 庫允許使用註釋。例如,Ruby 的 JSON 模塊,以及 Java 的 Jackson 庫加上啟用 JsonParser.Feature.ALLOW_COMMENTS 功能,就能支持在 JSON 中使用 JavaScript 風格的註釋。然而這並不是標準,許多編輯器也不能正常支持 JSON 文件中的註釋,使得編輯配置文件比較困難。

過分嚴格

JSON 的標準非常嚴格。嚴格的目的是為了方便 JSON 解析器的實現,但我認為,它也影響了易讀性,而且或多或少地使得人類書寫 JSON 更困難。

低信噪比

與許多其他配置語言相比,JSON 的噪聲很大。許多標點符號儘管使得機器很容易實現,但完全不能幫助人類閱讀。具體來說,對於配置文件而言,對象中的鍵幾乎必然是標識符,因此鍵的引號是完全不必要的。

而且 JSON 要求在整個文檔外面使用大括號,這樣做的目的是使之(幾乎)成為 JavaScript 的子集,並且在發送多個 JSON 對象時易於分隔各個對象。但作為配置文件,最外層的大括號是完全無用的。鍵值對之間的逗號大部分情況下也沒有用。通常,每一行就是一個鍵值對,所以完全可以把換行符作為分隔符。

說起逗號,JSON 還不支持末尾逗號。如果要求每個鍵值對都以逗號結尾,那麼至少應該接受末尾逗號吧,因為末尾逗號使得在末尾添加新項目很容易,而且能更容易地比較不同版本的差異。

過長的字符串

JSON 作為配置文件的另一個問題是它不支持多行字符串。如果要在字符串中使用換行,就得轉義成“\\n”,而且更糟糕的是,如果想在文件中寫一個跨行的字符串,那是完全沒辦法的。若是配置文件裡沒有特別長的字符串的話還好。但如果配置文件裡包含長字符串,比如某個項目的描述,或者 GPG 秘鑰,你肯定不希望把所有行都放在同一行裡並用“\\n”連接起來。

數字

此外,JSON對於數字的定義在某些場合下很有問題。根據JSON標準,數字是任意精度的有限浮點數,以十進制表示。大多數情況下這沒什麼問題。但如果你需要十六進制表示,或者需要無窮大、NaN等值,那麼TOML或YAML能更好地處理這些輸入。

1{

2 "name": "example",

3 "description": "A really long description that needs multiple lines.\\nThis is a sample project to illustrate why JSON is not a good configuration format. This description is pretty long, but it doesn't have any way to go onto multiple lines.",

4 "version": "0.0.1",

5 "main": "index.js",

6 "//": "This is as close to a comment as you are going to get",

7 "keywords": ["example", "config"],

8 "scripts": {

9 "test": "./test.sh",

10 "do_stuff": "./do_stuff.sh"

11 },

12 "bugs": {

13 "url": "https://example.com/bugs"

14 },

15 "contributors": [{

16 "name": "John Doe",

17 "email": "[email protected]"

18 }, {

19 "name": "Ivy Lane",

20 "url": "https://example.com/ivylane"

21 }],

22 "dependencies": {

23 "dep1": "^1.0.0",

24 "dep2": "3.40",

25 "dep3": "6.7"

26 }

27}

為什麼 JSON 不適合作為配置語言?

那應該用什麼?

配置語言的選擇取決於應用程序。每種語言都有自己的長處和短處。下面這些語言都可以考慮。這些都是專用於配置文件的語言,都比原本用於數據的 JSON 要好。

TOML

TOML(https://github.com/toml-lang/toml)作為配置語言日漸流行。它的使用者包括Cargo(Rust的編譯工具)、pip(Python包管理器)和dep(golang 依賴管理器)。TOML 有點像 INI 格式,但與 INI 不同的是,它有自己的標準,而且對於嵌套結構有很好的支持。它要比 YAML 簡單得多,所以當配置文件很簡單時,TOML就很合適。但如果配置文件有大量嵌套結構,那麼 TOML 可能會顯得有點囉嗦,此時其他語言如 YAML、HOCON 可能是更好的選擇。

1name = "example"

2description = """

3A really long description that needs multiple lines.

4This is a sample project to illustrate why JSON is not a \\

5good configuration format. This description is pretty long, \\

6but it doesn't have any way to go onto multiple lines."""

7

8version = "0.0.1"

9main = "index.js"

10# This is a comment

11keywords = ["example", "config"]

12

13[bugs]

14url = "https://example.com/bugs"

15

16[scripts]

17

18test = "./test.sh"

19do_stuff = "./do_stuff.sh"

20

21[[contributors]]

22name = "John Doe"

23email = "[email protected]"

24

25[[contributors]]

26name = "Ivy Lane"

27url = "https://example.com/ivylane"

28

29[dependencies]

30

31dep1 = "^1.0.0"

32# Why we depend on dep2

33dep2 = "3.40"

34dep3 = "6.7"

HJSON

HJSON(https://hjson.org/)是個基於 JSON 的語言,但更靈活,因此更容易閱讀。它支持註釋、多行字符串、不帶引號的鍵和字符串,以及可選的逗號。如果你喜歡 JSON 的簡單結構,但希望更適合配置文件的話,可以試試HJSON。它還提供命令行工具將 HJSON 轉換成 JSON,因此如果你的工具要求 JSON 的話,你可以用 HJSON 寫配置文件,並在構建過程中轉換成 JSON。與 HJSON 相似的另一個選擇是 JSON5(https://json5.org/)。

1{

2 name: example

3 description: '''

4 A really long description that needs multiple lines.

5

6 This is a sample project to illustrate why JSON is

7 not a good configuration format. This description

8 is pretty long, but it doesn't have any way to go

9 onto multiple lines.

10 '''

11 version: 0.0.1

12 main: index.js

13 # This is a a comment

14 keywords: ["example", "config"]

15>

16 test: ./test.sh

17 do_stuff: ./do_stuff.sh

18 }

19 bugs: {

20 url: https://example.com/bugs

21 }

22 contributors: [{

23 name: John Doe

24 email: [email protected]

25 } {

26 name: Ivy Lane

27 url: https://example.com/ivylane

28 }]

29 dependencies: {

30 dep1: ^1.0.0

31 # Why we have this dependency

32 dep2: "3.40"

33 dep3: "6.7"

34 }

35}

HOCON

HOCON(https://github.com/lightbend/config/blob/master/HOCON.md)是為Play框架(https://www.playframework.com/)設計的配置語言,但在Scala項目中非常流行。它是JSON的超集,所以可以兼容JSON文件。除了註釋、可選逗號、多行字符串等標準功能之外,HOCON還支持從其他文家中導入,引用其他值的鍵以避免重複代碼,以及使用點分隔的鍵指定到某個值的路徑,這樣用戶就不需要把所有值直接放在大括號對象裡了。

1name = example

2description = """

3A really long description that needs multiple lines.

4

5This is a sample project to illustrate why JSON is

6not a good configuration format. This description

7is pretty long, but it doesn't have any way to go

8onto multiple lines.

9"""

10version = 0.0.1

11main = index.js

12# This is a a comment

13keywords = ["example", "config"]

14scripts {

15 test = ./test.sh

16 do_stuff = ./do_stuff.sh

17}

18bugs.url = "https://example.com/bugs"

19contributors = [

20 {

21 name = John Doe

22 email = [email protected]

23 }

24 {

25 name = Ivy Lane

26 url = "https://example.com/ivylane"

27 }

28]

29dependencies {

30 dep1 = ^1.0.0

31 # Why we have this dependency

32 dep2 = "3.40"

33 dep3 = "6.7"

34}

YAML

YAML(YAML Ain't Markup Language,“YAM不是標記語言”,http://yaml.org/)是個非常靈活的格式,它幾乎是JSON的超集,許多著名的項目都使用YAML,如Travis CI、Circle CI和AWS CloudFormation。YAML的庫幾乎和JSON一樣流行。除了支持註釋、換行符分隔、多行字符串、不帶引號的字符串和更靈活的類型系統之外,YAML還可以引用之前定義過的結構,從而避免代碼重複。

YAML的主要缺點就是它的標準太複雜,導致不同實現之間的不一致。它的縮進層次還有重要的語法意義(類似於Python),這也是褒貶不一的地方。複製粘貼YAML也比較困難。關於YAML的缺點可以參考“也許不是那麼好的YAML”(https://arp242.net/weblog/yaml_probably_not_so_great_after_all.html)一文。

1name: example

2description: >

3 A really long description that needs multiple lines.

4

5 This is a sample project to illustrate why JSON is not a good

6 configuration format. This description is pretty long, but it

7 doesn't have any way to go onto multiple lines.

8version: 0.0.1

9main: index.js

10# this is a comment

11keywords:

12 - example

13 - config

14scripts:

15 test: ./test.sh

16 do_stuff: ./do_stuff.sh

17bugs:

18 url: "https://example.com/bugs"

19contributors:

20 - name: John Doe

21 email: [email protected]

22 - name: Ivy Lane

23 url: "https://example.com/ivylange"

24dependencies:

25 dep1: ^1.0.0

26 # Why we depend on dep2

27 dep2: "3.40"

28 dep3: "6.7"

腳本語言

如果你的應用程序是用某種腳本語言寫的,如 Python 或 Ruby,而且配置文件的來源可信,那麼最好的選擇就是用那種語言本身編寫配置文件。對於編譯語言,如果需要真正靈活的配置選項,也可以嵌入類似 Lua 這種腳本語言。這樣可以享受到腳本語言的靈活性,而且比使用另一種配置文件更容易。但使用腳本語言的缺點就是它可能過於強大,而且如果配置語言的來源不可信,這樣做會引入很嚴重的安全問題。

編寫自己的配置語言

如果出於某種原因,鍵值配置格式不符合你的要求,並且由於性能或大小限制不能使用腳本語言,那麼可能自己寫一個配置格式更合適。但在這樣做之前,務必要認真考慮,因為這樣做不僅需要自行維護解析器,還需要讓你的用戶熟悉一種新的配置格式才行。

為什麼 JSON 不適合作為配置語言?

結論

有了這麼多配置語言的選擇,使用JSON並不是個好注意。如果你要創建新的應用、框架或庫,需要一個配置文件,那麼選擇JSON之外的格式吧。

原文:https://www.lucidchart.com/techblog/2018/07/16/why-json-isnt-a-good-configuration-language/

作者:Thayne McCombs© Lucidchart。

“徵稿啦!”

CSDN 公眾號秉持著「與千萬技術人共成長」理念,不僅以「極客頭條」、「暢言」欄目在第一時間以技術人的獨特視角描述技術人關心的行業焦點事件,更有「技術頭條」專欄,深度解讀行業內的熱門技術與場景應用,讓所有的開發者緊跟技術潮流,保持警醒的技術嗅覺,對行業趨勢、技術有更為全面的認知。

如果你有優質的文章,或是行業熱點事件、技術趨勢的真知灼見,或是深度的應用實踐、場景方案等的新見解,歡迎聯繫 CSDN 投稿,聯繫方式:微信(guorui_1118,請備註投稿+姓名+公司職位),郵箱([email protected])。


分享到:


相關文章: