TOML 与 YAML 与 StrictYAML

Qiu*_*ang 10 python configuration yaml toml

TOML 说: “TOML 和 YAML 都强调人类可读性特征,例如注释可以更容易地理解给定行的目的。TOML 在组合这些方面有所不同,允许注释(与 JSON 不同)但保留简单性(与 YAML 不同)。”

我可以看到 TOML 不依赖重要的空格,但除此之外,我不确定它声称的 简单性。那究竟是什么?

然后我看到StrictYAML,“StrictYAML 是一个类型安全的 YAML 解析器,它解析和验证 YAML 规范的一个受限子集。” 类型安全,那到底是什么(再次)?TOML 没有为 YAML 修复而 StrictYAML 认为他有什么问题?我确实阅读了 StrictYAML 网站上的文章,但我仍然不清楚。

所以 TOML 和 StrictYAML 都想解决 YAML 的“问题”。但是除了缩进,还有什么问题?

- - 更新 - -

我在 reddit 上发现 StrictYaml 的作者谈到了YAML 与 TOML。但到目前为止我得到的答案是“strictyaml 显示对 YAML 的理解相当差”

YAML 的缺点:

隐式类型会导致意外的类型更改。(例如,把 3 放在你之前有一个字符串的地方,它会神奇地变成一个 int)。

一堆令人讨厌的“隐藏功能”,例如节点锚点和引用,使其看起来不清楚(尽管公平地说,很多人不使用它)。

TOML的缺点:

更嘈杂的语法(尤其是多行字符串)。

数组/表的处理方式令人困惑,尤其是表数组。

我写了一个库,它删除了我不喜欢 YAML 的大部分讨厌的东西,留下了我喜欢的核心。它与一堆其他配置格式之间进行了非常详细的比较,例如:https : //github.com/crdoconnor/strictyaml/blob/master/FAQ.rst#why-not-use-toml

Mar*_*son 26

StrictYAML 类型安全——“挪威问题”

以下是 StrictYAML 人员给出的示例:

countries:
- FR
- DE
- NO
Run Code Online (Sandbox Code Playgroud)

隐式将“NO”转换为 False 值。

>>> from pyyaml import load
>>> load(the_configuration)
{'countries': ['FR', 'DE', False]}
Run Code Online (Sandbox Code Playgroud)

https://hitchdev.com/strictyaml/why/implicit-typing-removed

  • 该“load”调用的*实际*输出还包括“<stdin>:1:YAMLLoadWarning:不推荐在不带 Loader=... 的情况下调用 yaml.load(),因为默认 Loader 不安全。请阅读 https://msg.pyyaml.org/load 了解完整详细信息。`。您可以轻松地执行“load(the_configuration, Loader=yaml.BaseLoader)”来避免这种情况,如警告链接的页面中所述。这修复了该 StrictYAML 页面上描述的所有问题。 (8认同)

fly*_*lyx 17

这可能是一个固执的答案,因为我已经编写了多个 YAML 实现。


替代方案解决了对 YAML 的常见批评

YAML 突出的语义特征是它可以表示一个可能的循环图。此外,YAML 映射可以使用复杂节点(序列或映射)作为键。当您想要表示任意数据结构时,这些功能是您可能需要的。

另一个奇特的 YAML 功能是tags。他们的目标是抽象不同编程语言中的不同类型,例如,!!mapadict在 Python 中是 a而object在 JavaScript 中是 a 。虽然很少显式使用,但隐式标签解析是为什么false通常作为布尔值droggeljug加载而作为字符串加载的原因。这里的明显目标是通过不需要!!bool false在每个字符串值上写入布尔值或强制引号来减少噪音。

然而,现实表明很多人对此感到困惑,而 YAML 定义的yes可能被解析为布尔值也无济于事。YAML 1.2 试图通过描述您可以使用的不同模式来解决这个问题,其中基本的“故障安全”模式专门加载到映射、序列和字符串,而更复杂的“JSON”和“核心”模式进行额外的类型猜测。然而,大多数 YAML 实现,尤其是 PyYAML,在 YAML 1.1 上保留了很长时间(许多实现最初是重写 PyYAML 代码,例如 libyaml、SnakeYAML)。这巩固了 YAML 做出需要修复的有问题的类型决策的观点。

如今,一些实现得到了改进,您可以使用故障安全模式来避免不需要的布尔值。在这方面,StrictYAML 将自身限制为故障安全模式;不要相信它的论点,即这是 PyYAML 无法做到的一些新奇事物。

YAML 实现的一个常见安全问题是它们将标签映射到任意构造函数调用(您可以在此处阅读基于此的 Ruby on Rails 漏洞利用)。请注意,这不是 YAML 的缺点;YAML 不建议在任何地方的对象构造过程中调用未知函数。这里的基本问题是数据序列化是数据封装的敌人;如果您的编程语言提供构造函数作为构造对象的唯一方法,那么这就是您在反序列化数据时需要做的。这里的补救措施只是调用已知的构造函数,这是在一系列此类漏洞利用(另一个使用 SnakeYAML iirc)浮出水面之后广泛实施的。如今,要调用未知的构造函数,您需要使用DangerLoader在 PyYAML 中恰当命名的类。

TOML

TOML 的主要语义差异在于它不支持循环、复杂键或标签。这意味着虽然您可以在任意用户定义的类中加载 YAML,但您始终将 TOML 加载到包含数据的数组中。

例如,虽然YAML允许你加载{foo: 1, bar: 2}到一个类的对象foobar整型字段,TOML总是这样加载到一个表。您通常在文档中发现的 YAML 功能的一个突出示例是它可以将标量加载1d6到对象中{number: 1, sides: 6};TOML 将始终将其加载为 string "1d6"

TOML 在这里被认为的简单性是它不做 YAML 做的一些事情。例如,如果您使用静态类型语言(如 Java),则在加载{foo: 1, bar: 2}到对象后myObject,您可以myObject.foo安全地访问(获取整数1)。如果您使用了 TOML,则需要这样做myObject["foo"],,如果密钥不存在,则可能会引发异常。这在 Python 等脚本语言中不太正确:这里,myObject.foo如果foo不是myObject.

我在这里回答很多 YAML 问题的观点是,人们不使用 YAML 的功能,并且经常将所有内容加载到类似Map<String, Object>(以 Java 为例)的结构中,然后从那里获取。如果你这样做,你也可以使用 TOML。

TOML 提供了另一种简单的语法:由于它比 YAML 简单得多,因此更容易发出用户可以理解的错误。例如,YAML 语法错误中的常见错误文本是“在此上下文中不允许映射值”(尝试在 SO 上搜索以查找大量问题)。你在这里得到这个例子:

foo: 1
  bar: 2
Run Code Online (Sandbox Code Playgroud)

错误消息不能帮助用户修复错误。这是因为 YAML 的复杂语法:YAML 认为1并且bar是多行标量的一部分(因为bar:缩进超过foo:),将它们放在一起,然后看到第二个:并失败,因为多行标量可能不会用作隐式键。然而,最有可能的是,用户要么是缩进的,要么是bar:认为他们可以给 foo ( 1) 和一些孩子一个标量值。由于 YAML 语法的可能性,很难编写可以帮助用户的错误消息。

由于 TOML 的语法要简单得多,因此错误消息更容易理解。如果编写 TOML 的用户不是具有语法分析背景的人,那么这是一个很大的优势。

TOML 比 YAML 有一个概念上的优势:由于它的结构允许较少的自由度,它往往更容易阅读。在阅读 TOML 时,您总是知道,“好吧,我将在其中包含带有值的嵌套表”,而使用 YAML,您有一些任意结构。我相信这在阅读 YAML 文件时需要更多的认知负荷。

严格的YAML

StrictYAML 认为它提供了类型安全性,但由于 YAML 不是一种编程语言,并且特别不支持赋值,因此基于由 StrictYAML 链接的维基百科定义,这种说法没有任何意义(类型安全性来使用您使用的编程语言;例如,任何 YAML 在将其加载到适当的 Java 类实例中后都是类型安全的,但在 Python 之类的语言中,您永远不会是类型安全的)。回顾其已删除功能列表,它显示对 YAML 的理解相当差:

  • 隐式类型:可以在 YAML 实现中使用故障安全模式停用,如上所述。
  • 对象的直接表示:它只是链接到 Ruby on Rails 事件,这意味着这是无法避免的,即使今天大多数实现在不删除该功能的情况下是安全的。
  • Duplicate Keys Disallowed:YAML 规范已经要求这样做。
  • Node anchors and refs:StrictYAML 认为使用它进行重复数据删除对于非程序员来说是不可读的,忽略了其意图是能够序列化循环结构,这在没有锚点和别名的情况下是不可能的。

在反序列化方面,

所有数据都是字符串、列表或 OrderedDict

它基本上与 TOML 支持的结构相同(我相信 StrictYAML 支持映射中的复杂键,因为在 Python 中既不能list也不能哈希OrderedDict)。

您还失去了反序列化为预定义类结构的能力。有人可能会说,由于无法构造一个类的对象具有良好定义的两个领域,因此StrictYAML较少类型安全比标准YAML:标准的YAML实现能保证返回的对象由类型描述一定的结构,而StrictYAML让你在每级别可以是字符串、列表或 OrderedDict 并且您不能做任何事情来限制它。

虽然它的一些论点有缺陷,但由此产生的语言仍然可用。例如,使用 StrictYAML,您无需关心困扰某些 YAML 实现的十亿笑声攻击。同样,这不是 YAML 问题而是实现问题,因为 YAML 不需要实现来复制从多个位置锚定和引用的节点。

底线

相当多的 YAML 问题源于糟糕的实现,而不是语言本身的问题。然而,YAML 作为一种语言肯定是复杂的,并且语法错误可能难以理解,这可能是使用像 TOML 这样的语言的正当理由。至于 StrictYAML,它确实提供了一些好处,但我建议不要使用它,因为它没有适当的规范,而且只有一个实现,这是一个很容易成为维护噩梦的设置(项目可能会中断,破坏更改很容易)。

  • @Qiulang我想说TOML对于深度嵌套的结构来说是不好的,因为每次都必须给出完整表的路径使得很难理解数据的结构。您可以缩进代码来稍微解决这个问题,但是完全成熟的路径会严重违反 DRY。如果您不喜欢明显的缩进,您仍然可以使用带有流语法的 YAML;如果 YAML 有一个选项来禁止块(基于缩进)语法,恕我直言,这通常是更好的选择。 (3认同)
  • @Qiulang,我认为结构缩进是一个有趣但最终有缺陷的概念(不仅适用于 YAML,也适用于 Python 或 Nim)。如果没有结束结构的可见标记,代码的嵌套越多且长度越长,代码就越难以阅读。虽然在 Python 中您可以重组代码来对抗过度嵌套,但这在 YAML 中实际上是不可能的。然而,对于 C 的“}”(相对于 Pascal 的“end &lt;name&gt;;”)也提出了类似的论点,并且大多数现代语言仍然使用“}”,因此它似乎是个人偏好。 (2认同)
  • 但至少TOML不存在这个问题。也许这就是其“简单”的原因之一? (2认同)