Jsonnet 的std.mergePatch
实现RFC7396,但在我的幼稚测试中,我没有发现它的行为方式和+
操作员之间有什么不同;例如,+
运算符尊重x+
语法。在 Jsonnetstd.mergePatch
本身中实现,这似乎暗示它与运算符不同,我假设它是内置的。+
这两种合并方式的语义有何不同?
Jsonnet 的+
和std.mergePatch
是完全不同的操作。+
运算符仅在单个级别上进行操作,并递归std.mergePatch
地遍历对象并合并嵌套对象。用一个例子来解释是最简单的:
local foo = { a: {b1: {c1: 42}}},
bar = { a: {b2: {c2: 2}}};
foo + bar
输出:
{
"a": {
"b2": {
"c2": 2
}
}
}
注意bar.a
完全替换foo.a
。使用+
第二个对象中的所有字段覆盖第一个对象中的字段。将其与使用的结果进行比较std.mergePatch(foo, bar)
。
{
"a": {
"b1": {
"c1": 42
},
"b2": {
"c2": 2
}
}
}
由于两者foo
都有bar
一个字段a
,因此被合并,最终结果同时包含b1
和b2
。
所以重申一下,+
这是一个“平面”操作,它用第二个对象的字段覆盖第一个对象的字段。
不过,这还不是故事的结局。您提到field+: value
了语法,我将尝试解释它的真正作用。在 Jsonnet+
中不仅仅是覆盖,而是 OO 意义上的继承。它创建一个对象,该对象是第二个对象继承自第一个对象的结果。有一个操作符有点奇怪——在所有主流语言中,这种关系都是静态定义的。在 Jsonnet 中,当您这样做时foo + bar
,对象可以通过以下bar
方式访问内容:foo
super
{ a: 2 } + { a_plus_1: super.a + 1}
这导致:
{
"a": 2,
"a_plus_1": 3
}
您可以使用此功能来合并更深处的字段:
{ a: {b: {c1: 1}, d: 1}} +
{ a: super.a + {b: {c2: 2} } }
导致:
{
"a": {
"b": {
"c2": 2
},
"d": 1
}
}
不过,这有点重复(如果字段名称更长,那会很烦人)。所以我们有一个很好的语法糖:
{ a: {b: {c1: 1} , d: 1}} +
{ a+: {b: {c2: 2}} }
请注意,在这些示例中,我们只对我们选择的一个特定字段进行了合并。我们仍然替换了 的值a.b
。这更加灵活,因为在许多情况下,您不能天真地合并里面的所有东西(有时嵌套对象是“原子的”,应该完全替换)。
中的版本与带有的版本的+:
工作方式相同super
。细微的区别在于它+:
实际上转换为类似的东西,因此当根本没有提供或没有此特定字段if field in super then super.field + val else val
时,它也会返回相同的值。super
例如{a +: {b: 42}}
评估就好了{a: { b: 42 }}
。
强制布道:虽然+
很强大,但请不要滥用它。当您需要参数化某些东西时,请考虑使用函数而不是继承。