这可能是一个有偏见的答案,因为我编写了多个YAML实现。
替代方案中解决YAML常见批评的问题
YAML杰出的语义特性在于它可以表示可能是循环图的数据结构。此外,YAML映射可以使用复杂节点(序列或映射)作为键。当您想要表示任意数据结构时,这些功能就是您潜在需要的。
另一个奇特的YAML功能是标签。它们的目标是抽象化不同编程语言中的不同类型,例如!!map
在Python中将是dict
,但在JavaScript中将是object
。虽然很少明确使用,但隐式标记解析通常是false
被加载为布尔值而droggeljug
被加载为字符串的原因。这里的明显目标是通过不要求编写像!!bool false
这样的布尔值或在每个字符串值上强制使用引号来减少噪音。
然而,现实情况表明,许多人对此感到困惑,并且YAML定义的将yes
解析为布尔值的做法也没有帮助。 YAML 1.2试图通过描述您可以使用的不同模式来纠正这一点,其中基本的“failsafe”模式仅加载到映射、序列和字符串,而更复杂的“JSON”和“core”模式则进行了额外的类型猜测。然而,大多数YAML实现(特别是PyYAML)长时间以来都停留在YAML 1.1版本(许多实现最初是重写的PyYAML代码,例如libyaml,SnakeYAML)。这巩固了YAML做出可疑输入决策需要修复的观点。
现在,一些实现已经改进,您可以使用failsafe模式避免不需要的布尔值。在这方面,StrictYAML限制自己使用failsafe模式;不要相信它声称这是PyYAML无法做到的新颖之处。
YAML实现的常见安全问题是它们将标记映射到任意构造函数调用上(你可以在这里阅读有关基于此的Ruby on Rails漏洞的信息)。请注意,这不是YAML的缺陷;YAML没有建议在对象构造期间调用未知函数。这里的根本问题在于数据序列化与数据封装的对立;如果您的编程语言仅提供构造函数作为构造对象的唯一方法,则反序列化数据时必须执行此操作。解决方法只是调用已知的构造函数,在一系列此类漏洞(例如使用SnakeYAML的另一个漏洞)浮出水面后得到广泛实施。现在,要调用未知构造函数,您需要在PyYAML中使用一个名为DangerLoader
的类。
TOML
TOML的主要语义差异在于它不支持循环、复杂键或标记。这意味着虽然您可以将YAML加载到任意用户定义的类中,但始终会将TOML加载到包含数据的表格或数组中。
例如,虽然YAML允许您将
{foo: 1, bar: 2}
加载到一个具有
foo
和
bar
整数字段的类的对象中,但TOML始终会将其加载到表中。在文档中通常可以找到YAML功能的杰出示例是它可以将标量
1d6
加载到对象
{number: 1, sides: 6}
中;TOML始终将其作为字符串
"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>
这样的结构中,然后从那里继续。如果您这样做,您也可以使用TOML。
TOML提供了一种不同类型的简单性,即其语法:由于它比YAML简单得多,因此更容易发出用户可以理解的错误。例如,在YAML语法错误中常见的错误文本是“在此上下文中不允许映射值”(请尝试在SO上搜索此内容以找到大量问题)。例如,在此处:
foo: 1
bar: 2
错误信息不能帮助用户修复错误。这是因为YAML语法复杂:YAML认为
1
和
bar
是多行标量的一部分(因为
bar:
的缩进大于
foo:
),将它们放在一起,然后看到第二个
:
并失败,因为多行标量不能用作隐式键。然而,很可能用户只是将
bar:
缩进或以为可以同时给foo(
1
)和一些子元素一个标量值。由于YAML语法的可能性,编写有助于用户的错误消息将是困难的。
由于TOML的语法更简单,错误消息更容易理解。如果编写TOML的用户不需要具备解析语法的背景,则这是一个巨大的优势。
TOML在概念上优于YAML:由于其结构允许的自由度较小,因此阅读起来更容易。阅读TOML时,您总是知道,“好的,我将嵌套表格并在其中放置值”,而使用YAML时,则存在某种任意结构。我认为这需要更多的认知负荷来读取YAML文件。
StrictYAML认为它提供了类型安全性,但由于YAML不是编程语言,并且明确不支持赋值,因此根据StrictYAML链接的
Wikipedia定义,这种说法毫无意义(类型安全性随您使用的编程语言而来去;例如,在将任何YAML加载到适当的Java类实例中之后,任何YAML都是类型安全的,但在像Python这样的语言中永远不会是类型安全的)。通过查看
其删除功能列表,它显示了对YAML相当贫乏的理解:
-
隐式类型转换:可以在使用failsafe模式的YAML实现中停用,如上所述。
-
对象的直接表示:它只是链接到Ruby on Rails事件,暗示这是不可避免的,即使大多数实现今天都没有删除该功能也是安全的。
-
禁止重复键:YAML规范已经要求这样做。
-
节点锚点和引用:StrictYAML认为使用此功能进行去重对非程序员来说不可读,忽略了其意图是能够序列化循环结构,而没有锚点和别名是不可能的。
在反序列化方面,
- 所有数据都是字符串、列表或OrderedDict。
这基本上是TOML支持的相同结构(我认为StrictYAML支持映射中的复杂键,因为
list
和
OrderedDict
在Python中都不可哈希)。
您还失去了将数据反序列化为预定义类结构的能力。有人可能会认为,无法构造具有明确定义字段的类对象使StrictYAML比标准YAML
不够类型安全:标准YAML实现可以保证返回的对象具有由类型描述的某种结构,而StrictYAML在每个级别上都给你一个字符串、列表或OrderedDict,你不能做任何限制。
虽然它的一些论点是有缺陷的,但生成的语言仍然可用。例如,使用StrictYAML,您不需要担心
billion laughs attack困扰一些YAML实现。同样,这不是YAML的问题,而是实现的问题,因为YAML不要求实现复制一个在多个位置上被锚定和引用的节点。
底线
相当多的YAML问题源于糟糕的实现,而不是语言本身的问题。然而,作为一种语言,YAML确实很复杂,语法错误可能很难理解,这可能是使用像TOML这样的语言的一个有效理由。至于StrictYAML,它确实提供了一些好处,但我建议不要使用它,因为它没有适当的规范,只有一个实现,这是一个非常容易成为维护噩梦的设置(项目可能会停止,容易出现破坏性变化)。