在Jsonnet中,“+”操作符和std.mergePatch之间有什么区别?

6
Jsonnet的std.mergePatch实现了RFC7396,但在我的幼稚测试中,我没有发现它的行为方式与+运算符有任何不同;例如,+运算符会遵循x+语法。std.mergePatch本身就是在Jsonnet中实现的,这似乎意味着它与内置的+运算符不同。
这两种合并方式的语义有什么不同?
1个回答

11

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
      }
   }
}

由于foobar都有一个字段a,因此它们被合并,最终结果包含了b1b2

因此,再次强调,+是一种“扁平”操作,它用第二个对象的字段覆盖第一个对象的字段。

然而,这还不是全部。你提到了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 }}
强制布道:虽然+非常强大,请不要滥用它。在需要参数化某些内容时,请考虑使用函数而不是继承。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接