Jsonnet中的+运算符和std.mergePatch有什么区别?

What is the difference between the + operator and std.mergePatch in Jsonnet?

Jsonnet 的 std.mergePatch implements RFC7396, but in my naive testing I didn't find a different between the way it behaved and the + operator; e.g. the + operator respects x+ syntax. std.mergePatch is implemented in Jsonnet 本身,这似乎暗示它不同于 + 运算符,我假设后者是内置的。

这两种合并方式的语义有何不同?

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

所以重申一下,+ 是一个 "flat" 操作,它用第二个对象的字段覆盖第一个对象的字段。

不过,这还没有结束。您提到了 field+: value 语法,我将尝试解释它的实际作用。在 Jsonnet 中 + 不仅仅是覆盖,而是 OO 意义上的继承。它创建一个对象,该对象是第二个对象继承自第一个对象的结果。拥有一个运算符有点奇怪——在所有主流语言中,这种关系都是静态定义的。在 Jsonnet 中,当您执行 foo + bar 时,bar 对象可以访问从 foosuper:

的内容
{ 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的值。这要灵活得多,因为在很多情况下你不能天真地将所有东西合并到里面(有时嵌套对象是 "atomic" 并且应该完全替换)。

+: 中的版本与 super 中的版本的工作方式相同。细微的区别在于 +: 实际上转换为 if field in super then super.field + val else val 之类的东西,因此当根本没有提供 super 或没有此特定内容时,它也 returns 相同的值场地。例如 {a +: {b: 42}} 可以很好地评估 {a: { b: 42 }}.

强制布道:+虽然很厉害,但请不要滥用。当你需要参数化某些东西时,考虑使用函数而不是继承。