许多项目用 JSON 作为配置文件。大概最著名的例子便是 npm 和 yarn 利用的 package.json,还有许多其他项目也用 JSON,如 CloudFormation(最初仅支持 JSON,现在还支持 YAML)和 composer(PHP)。
但是,有几个缘故原由表明 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\"大众: \"大众johndoe@example.com\"大众
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 要好。
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 = \"大众johndow@example.com\"大众
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 scripts: {
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: johndoe@example.com
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 = johndoe@example.com
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: johndoe@example.com
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之外的格式吧。
原文:https://www.lucidchart.com/techblog/2018/07/16/why-json-isnt-a-good-configuration-language/
作者:Thayne McCombs© Lucidchart。
译者:弯月,责编:屠敏
“征稿啦!
”
CSDN 公众年夜众号秉持着「与千万技能人共发展」理念,不仅以「极客头条」、「畅言」栏目在第一韶光以技能人的独特视角描述技能人关心的行业焦点事宜,更有「技能头条」专栏,深度解读行业内的热门技能与场景运用,让所有的开拓者紧跟技能潮流,保持警觉的技能嗅觉,对行业趋势、技能有更为全面的认知。
如果你有优质的文章,或是行业热点事宜、技能趋势的真知灼见,或是深度的运用实践、场景方案等的新见地,欢迎联系 CSDN 投稿,联系办法:微信(guorui_1118,请备注投稿+姓名+公司职位),邮箱(guorui@csdn.net)。