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
}
}
}
由于foo
和bar
都有一个字段a
,因此合并,最终结果包含b1
和b2
。
所以重申一下,+
是一个 "flat" 操作,它用第二个对象的字段覆盖第一个对象的字段。
不过,这还没有结束。您提到了 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
的值。这要灵活得多,因为在很多情况下你不能天真地将所有东西合并到里面(有时嵌套对象是 "atomic" 并且应该完全替换)。
+:
中的版本与 super
中的版本的工作方式相同。细微的区别在于 +:
实际上转换为 if field in super then super.field + val else val
之类的东西,因此当根本没有提供 super
或没有此特定内容时,它也 returns 相同的值场地。例如 {a +: {b: 42}}
可以很好地评估 {a: { b: 42 }}
.
强制布道:+
虽然很厉害,但请不要滥用。当你需要参数化某些东西时,考虑使用函数而不是继承。
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
}
}
}
由于foo
和bar
都有一个字段a
,因此合并,最终结果包含b1
和b2
。
所以重申一下,+
是一个 "flat" 操作,它用第二个对象的字段覆盖第一个对象的字段。
不过,这还没有结束。您提到了 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
的值。这要灵活得多,因为在很多情况下你不能天真地将所有东西合并到里面(有时嵌套对象是 "atomic" 并且应该完全替换)。
+:
中的版本与 super
中的版本的工作方式相同。细微的区别在于 +:
实际上转换为 if field in super then super.field + val else val
之类的东西,因此当根本没有提供 super
或没有此特定内容时,它也 returns 相同的值场地。例如 {a +: {b: 42}}
可以很好地评估 {a: { b: 42 }}
.
强制布道:+
虽然很厉害,但请不要滥用。当你需要参数化某些东西时,考虑使用函数而不是继承。