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

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

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

但是混坞,JSON 實(shí)際上是一種非常糟糕的配置語言。別誤會(huì)我的意思钢坦,我其實(shí)是喜歡 JSON 的究孕。它是一種相對(duì)靈活的文本格式,對(duì)于機(jī)器和人類來說都很容易閱讀场钉,而且是一種非常好的數(shù)據(jù)交換和存儲(chǔ)格式蚊俺。但作為一種配置語言懈涛,它有它的不足逛万。

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

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

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

JSON 的問題

缺乏注釋

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

我們可以通過一些方法給 JSON 添加注釋锚沸。一種常見的方法是在對(duì)象中使用特殊的鍵作為注釋跋选,例如“//”或“__comment”。但是哗蜈,這種語法的可讀性不高野建,并且為了在單個(gè)對(duì)象中包含多個(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 解析器會(huì)這么簡(jiǎn)單陷猫,但在我看來,它還會(huì)影響可讀性的妖,并且在較小程度上會(huì)影響可寫性绣檬。

低信噪比

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

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

說到逗號(hào)罕偎,JSON 居然不允許在結(jié)尾出現(xiàn)逗號(hào)。如果你需要在每個(gè)鍵值對(duì)之后使用逗號(hào)京闰,那么至少應(yīng)該接受結(jié)尾的逗號(hào)颜及,因?yàn)橛辛私Y(jié)尾的逗號(hào),在添加新條目時(shí)會(huì)更容易蹂楣,而且在進(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 對(duì)數(shù)字的定義可能會(huì)有問題惭墓。JSON 規(guī)范中將數(shù)字定義成使用十進(jìn)制表示的任意精度有限浮點(diǎn)數(shù)坛梁。對(duì)于大多數(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)官帘。它支持注釋、多行字符串昧谊、不帶引號(hào)的鍵和字符串刽虹,以及可選的逗號(hào)。如果你想要 JSON 結(jié)構(gòu)的簡(jiǎn)單性呢诬,同時(shí)對(duì)配置文件更友好涌哲,那么可以考慮 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 文件。除了注釋澳迫、可選逗號(hào)和多行字符串這些標(biāo)準(zhǔn)特性外局齿,HOCON 還支持從其他文件導(dǎo)入和引用其他值的鍵,避免重復(fù)代碼橄登,并使用以點(diǎn)作為分隔符的鍵來指定值的路徑抓歼,因此用戶可以不必將所有值直接放在花括號(hào)對(duì)象中。

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)司志,有些人喜歡,有些人不喜歡降宅。這會(huì)讓復(fù)制和粘貼變得很麻煩俐芯。

腳本語言

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

自定義配置格式

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

結(jié)論

有了這么多更好的配置語言抢肛,沒有理由還要使用 JSON狼钮。如果要?jiǎng)?chuàng)建需要用到配置的新應(yīng)用程序、框架或庫捡絮,請(qǐ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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市福稳,隨后出現(xiàn)的幾起案子涎拉,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曼库,死亡現(xiàn)場(chǎng)離奇詭異区岗,居然都是意外死亡略板,警方通過查閱死者的電腦和手機(jī)毁枯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叮称,“玉大人种玛,你說我怎么就攤上這事∪块埽” “怎么了赂韵?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挠蛉。 經(jīng)常有香客問我祭示,道長,這世上最難降的妖魔是什么谴古? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任质涛,我火速辦了婚禮,結(jié)果婚禮上掰担,老公的妹妹穿的比我還像新娘汇陆。我一直安慰自己,他們只是感情好带饱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布毡代。 她就那樣靜靜地躺著,像睡著了一般勺疼。 火紅的嫁衣襯著肌膚如雪教寂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天执庐,我揣著相機(jī)與錄音孝宗,去河邊找鬼。 笑死耕肩,一個(gè)胖子當(dāng)著我的面吹牛因妇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猿诸,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婚被,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了梳虽?” 一聲冷哼從身側(cè)響起址芯,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后谷炸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體北专,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年旬陡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拓颓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡描孟,死狀恐怖驶睦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匿醒,我是刑警寧澤场航,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站廉羔,受9級(jí)特大地震影響溉痢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜憋他,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一孩饼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧举瑰,春花似錦捣辆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至耸序,卻和暖如春忍些,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坎怪。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工罢坝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搅窿。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓嘁酿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親男应。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闹司,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

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