附:為什么說 JSON 不適合做配置文件?

學(xué)習(xí)完整課程請移步 互聯(lián)網(wǎng) Java 全棧工程師

很多項(xiàng)目使用 JSON 作為配置文件,最明顯的例子就是 npm 和 yarn 使用的 package.json 文件。當(dāng)然,還有很多其他文件,例如 CloudFormation(最初只有 JSON,但現(xiàn)在也支持 YAML)和 composer(PHP)。

但是,JSON 實(shí)際上是一種非常糟糕的配置語言。別誤會我的意思,我其實(shí)是喜歡 JSON 的。它是一種相對靈活的文本格式,對于機(jī)器和人類來說都很容易閱讀,而且是一種非常好的數(shù)據(jù)交換和存儲格式。但作為一種配置語言,它有它的不足。

為什么流行使用 JSON 作為配置語言?

將 JSON 用作配置文件有幾個(gè)方面的原因,其中最大的原因可能是它很容易實(shí)現(xiàn)。很多編程語言的標(biāo)準(zhǔn)庫都支持 JSON,開發(fā)人員或用戶可能已經(jīng)很熟悉 JSON,所以不需要學(xué)習(xí)新的配置格式就可以使用那些產(chǎn)品?,F(xiàn)在幾乎所有的工具都提供 JSON 支持,包括語法突出顯示、自動(dòng)格式化、驗(yàn)證工具等。

這些都是很好的理由,但這種無處不在的格式其實(shí)不適合用作配置。

JSON 的問題

缺乏注釋

注釋對于配置語言而言絕對是一個(gè)重要的功能。注釋可用于標(biāo)注不同的配置選項(xiàng)、解釋為什么要配置成特定的值,更重要的是,在使用不同的配置進(jìn)行測試和調(diào)試時(shí)需要臨時(shí)注釋掉部分配置。當(dāng)然,如果只是把 JSON 當(dāng)作是一種數(shù)據(jù)交換格式,那么就不需要用到注釋。

我們可以通過一些方法給 JSON 添加注釋。一種常見的方法是在對象中使用特殊的鍵作為注釋,例如“//”或“__comment”。但是,這種語法的可讀性不高,并且為了在單個(gè)對象中包含多個(gè)注釋,需要為每個(gè)注釋使用唯一的鍵。David Crockford(JSON 的發(fā)明者)建議使用預(yù)處理器來刪除注釋。如果你的應(yīng)用程序需要使用 JSON 作為配置,那么完全沒問題,不過這確實(shí)帶來了一些額外的工作量。

一些 JSON 庫允許將注釋作為輸入。例如,Ruby 的 JSON 模塊和啟用了 JsonParser.Feature.ALLOW_COMMENTS 功能的 Java Jackson 庫可以處理 JavaScript 風(fēng)格的注釋。但是,這不是標(biāo)準(zhǔn)的方式,而且很多編輯器無法正確處理 JSON 文件中的注釋,這讓編輯它們變得更加困難。

過于嚴(yán)格

JSON 規(guī)范非常嚴(yán)格,這也是為什么實(shí)現(xiàn) JSON 解析器會這么簡單,但在我看來,它還會影響可讀性,并且在較小程度上會影響可寫性。

低信噪比

與其他配置語言相比,JSON 顯得非常嘈雜。JSON 的很多標(biāo)點(diǎn)符號對可讀性毫無幫助,況且,對象中的鍵幾乎都是標(biāo)識符,所以鍵的引號其實(shí)是多余的。

此外,JSON 需要使用花括號將整個(gè)文檔包圍起來,所以 JSON 是 JavaScript 的子集,并在流中發(fā)送多個(gè)對象時(shí)用于界定不同的對象。但是,對于配置文件來說,最外面的大括號其實(shí)沒有任何用處。在配置文件中,鍵值對之間的逗號也是沒有必要的。通常情況下,每行只有一個(gè)鍵值對,所以使用換行作為分隔符更有意義。

說到逗號,JSON 居然不允許在結(jié)尾出現(xiàn)逗號。如果你需要在每個(gè)鍵值對之后使用逗號,那么至少應(yīng)該接受結(jié)尾的逗號,因?yàn)橛辛私Y(jié)尾的逗號,在添加新條目時(shí)會更容易,而且在進(jìn)行 commit diff 時(shí)也更清晰。

長字符串

JSON 作為配置格式的另一個(gè)問題是,它不支持多行字符串。如果你想在字符串中換行,必須使用 “\n” 進(jìn)行轉(zhuǎn)義,更糟糕的是,如果你想要一個(gè)字符串在文件中另起一行顯示,那就徹底沒辦法了。如果你的配置項(xiàng)里沒有很長的字符串,那就不是問題。但是,如果你的配置項(xiàng)里包括了長字符串,例如項(xiàng)目描述或 GPG 密鑰,你可能不希望只是使用 “\n” 來轉(zhuǎn)義而不是使用真實(shí)的換行符。

數(shù)字

此外,在某些情況下,JSON 對數(shù)字的定義可能會有問題。JSON 規(guī)范中將數(shù)字定義成使用十進(jìn)制表示的任意精度有限浮點(diǎn)數(shù)。對于大多數(shù)應(yīng)用程序來說,這沒有問題。但是,如果你需要使用十六進(jìn)制表示法或表示無窮大或 NaN 等值時(shí),那么 TOML 或 YAML 將能夠更好地處理它們。

{

  "name": "example",

  "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.",

  "version": "0.0.1",

  "main": "index.js",

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

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

  "scripts": {

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

    "do_stuff": "./do_stuff.sh"

  },

  "bugs": {

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

  },

  "contributors": [{

    "name": "John Doe",

    "email": "johndoe@example.com"

  }, {

    "name": "Ivy Lane",

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

  }],

  "dependencies": {

    "dep1": "^1.0.0",

    "dep2": "3.40",

    "dep3": "6.7"

  }

}

JSON 的替代方案

選擇哪一種配置語言取決于你的應(yīng)用程序。每種語言都有各自的優(yōu)缺點(diǎn),下面列出了一些可以考慮的選項(xiàng)。它們都是為配置而設(shè)計(jì)的語言,每一種都比 JSON 這樣的數(shù)據(jù)語言更好。

name = "example"

description = """

A really long description that needs multiple lines.

This 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."""



version = "0.0.1"

main = "index.js"

# This is a comment

keywords = ["example", "config"]



[bugs]

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



[scripts]



test = "./test.sh"

do_stuff = "./do_stuff.sh"



[[contributors]]

name = "John Doe"

email = "johndow@example.com"



[[contributors]]

name = "Ivy Lane"

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



[dependencies]



dep1 = "^1.0.0"

# Why we depend on dep2

dep2 = "3.40"

dep3 = "6.7"

HJSON

HJSON 是一種基于 JSON 的格式,但具有更大的靈活性,可讀性也更強(qiáng)。它支持注釋、多行字符串、不帶引號的鍵和字符串,以及可選的逗號。如果你想要 JSON 結(jié)構(gòu)的簡單性,同時(shí)對配置文件更友好,那么可以考慮 HJSON。有一些可以將 HJSON 轉(zhuǎn)換為 JSON 的命令行工具,如果你使用的工具是基于 JSON 的,可以先用 HJSON 編寫配置,然后再轉(zhuǎn)換成 JSON。JSON5 是另一個(gè)與 HJSON 非常相似的配置語言。

{

  name: example

  description: '''

  A really long description that needs multiple lines.

  This 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.

  '''

  version: 0.0.1

  main: index.js

  # This is a a comment

  keywords: ["example", "config"]

  scripts: {

    test: ./test.sh

    do_stuff: ./do_stuff.sh

  }

  bugs: {

    url: https://example.com/bugs

  }

  contributors: [{

    name: John Doe

    email: johndoe@example.com

  } {

    name: Ivy Lane

    url: https://example.com/ivylane

  }]

  dependencies: {

    dep1: ^1.0.0

    # Why we have this dependency

    dep2: "3.40"

    dep3: "6.7"

  }

}

HOCON

HOCON 是為 Play 框架設(shè)計(jì)的配置格式,在 Scala 項(xiàng)目中非常流行。它是 JSON 的超集,因此可以使用現(xiàn)有的 JSON 文件。除了注釋、可選逗號和多行字符串這些標(biāo)準(zhǔn)特性外,HOCON 還支持從其他文件導(dǎo)入和引用其他值的鍵,避免重復(fù)代碼,并使用以點(diǎn)作為分隔符的鍵來指定值的路徑,因此用戶可以不必將所有值直接放在花括號對象中。

name = example

description = """

A really long description that needs multiple lines.



This 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.

"""

version = 0.0.1

main = index.js

# This is a a comment

keywords = ["example", "config"]

scripts {

  test = ./test.sh

  do_stuff = ./do_stuff.sh

}

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

contributors = [

  {

    name = John Doe

    email = johndoe@example.com

  }

  {

    name = Ivy Lane

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

  }

]

dependencies {

  dep1 = ^1.0.0

  # Why we have this dependency

  dep2 = "3.40"

  dep3 = "6.7"

}

YAML

YAML(YAML 不是標(biāo)記語言)是一種非常靈活的格式,幾乎是 JSON 的超集,已經(jīng)被用在一些著名的項(xiàng)目中,如 Travis CI、Circle CI 和 AWS CloudFormation。YAML 的庫幾乎和 JSON 一樣無處不在。除了支持注釋、換行符分隔、多行字符串、裸字符串和更靈活的類型系統(tǒng)之外,YAML 也支持引用文件,以避免重復(fù)代碼。

YAML 的主要缺點(diǎn)是規(guī)范非常復(fù)雜,不同的實(shí)現(xiàn)之間可能存在不一致的情況。它將縮進(jìn)視為嚴(yán)格語法的一部分(類似于 Python),有些人喜歡,有些人不喜歡。這會讓復(fù)制和粘貼變得很麻煩。

腳本語言

如果你的應(yīng)用程序是使用 Python 或 Ruby 等腳本語言開發(fā)的,并且你知道配置的來源是可靠的,那么最好的選擇可能就是使用這些語言進(jìn)行配置。如果你需要一個(gè)真正靈活的配置選項(xiàng),也可以在編譯語言中嵌入諸如 Lua 之類的腳本語言。這樣可以獲得腳本語言的靈活性,而且比使用不同的配置語言更容易實(shí)現(xiàn)。使用腳本語言的缺點(diǎn)是它可能過于強(qiáng)大,當(dāng)然,如果配置來源是不受信任的,可能會引入嚴(yán)重的安全問題。

自定義配置格式

如果由于某種原因,鍵值配置格式不能滿足你的要求,并且由于性能或大小限制而無法使用腳本語言,那么可以考慮自定義配置格式。如果是這種情況,那么在做出選擇之前要想清楚,因?yàn)槟悴粌H要編寫和維護(hù)一個(gè)解析器,還要讓你的用戶熟悉另一種配置格式。

結(jié)論

有了這么多更好的配置語言,沒有理由還要使用 JSON。如果要?jiǎng)?chuàng)建需要用到配置的新應(yīng)用程序、框架或庫,請選擇 JSON 以外的其他選項(xiàng)。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 很多項(xiàng)目使用 JSON 作為配置文件,最明顯的例子就是 npm 和 yarn 使用的 package.json 文...
    elef閱讀 5,777評論 0 1
  • ESLint 配置 ESlint 被設(shè)計(jì)為完全可配置的,這意味著你可以關(guān)閉每一個(gè)規(guī)則而只運(yùn)行基本語法驗(yàn)證,或混合和...
    靜默虛空閱讀 41,855評論 3 14
  • 一、Python簡介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡介】: Python 是一個(gè)...
    _小老虎_閱讀 6,353評論 0 10
  • 愛, 是一個(gè)名詞? 還是個(gè)動(dòng)詞? 如果愛是一個(gè)名詞, 它是一見鐘情? 還是日久生情? 如果愛是一個(gè)動(dòng)詞, 它是朝夕...
    書書不是叔叔閱讀 200評論 0 0
  • 圖片發(fā)自簡書App 【晚 秋】 (七律) 夕照林泉悠曲徑,楓紅遠(yuǎn)岫獨(dú)瀟疏。 秋深涼月清樽酒,歲晚寒燈黃卷書。 此樂...
    06e9fe388a97閱讀 2,020評論 30 74

友情鏈接更多精彩內(nèi)容