6

Jsonnet 的std.mergePatch实现RFC7396,但在我的幼稚测试中,我没有发现它的行为方式和+操作员之间有什么不同;例如,+运算符尊重x+语法。在 Jsonnetstd.mergePatch本身中实现,这似乎暗示它与运算符不同,我假设它是内置的。+

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

4

1 回答 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
      }
   }
}

由于两者foo都有bar一个字段a,因此被合并,最终结果同时包含b1b2

所以重申一下,+这是一个“平面”操作,它用第二个对象的字段覆盖第一个对象的字段。

不过,这还不是故事的结局。您提到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。这更加灵活,因为在许多情况下,您不能天真地合并里面的所有东西(有时嵌套对象是“原子的”,应该完全替换)。

中的版本与带有的版本的+:工作方式相同super。细微的区别在于它+:实际上转换为类似的东西,因此当根本没有提供或没有此特定字段if field in super then super.field + val else val时,它也会返回相同的值。super例如{a +: {b: 42}}评估就好了{a: { b: 42 }}

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

于 2020-05-07T11:02:09.057 回答