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中,+
不仅仅是覆盖,而且是面向对象意义下的继承。它创建了一个对象,该对象是第二个对象从第一个对象继承而来的结果。在所有主流语言中,这样的关系都是静态定义的,但在Jsonnet中,当你执行foo+bar
时,bar
对象可以通过super
访问foo
的内容:
{ 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 }}
。
强制布道:虽然
+
非常强大,请不要滥用它。在需要参数化某些内容时,请考虑使用函数而不是继承。