Jsonnet's 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 itself, which seems to imply that it is different than the +
operator, which I'm assuming is a builtin.
What is different about the semantics of these two ways of merging?
Jsonnet's +
and std.mergePatch
are completely different operations. The +
operator operates only on a single level and std.mergePatch
traverses the object recursively and merges the nested objects. It's easiest to explain with an example:
local foo = { a: {b1: {c1: 42}}},
bar = { a: {b2: {c2: 2}}};
foo + bar
Output:
{
"a": {
"b2": {
"c2": 2
}
}
}
Note that the bar.a
completely replaced foo.a
. With +
all fields in the second object override the fields in the first object. Compare that with the result of using std.mergePatch(foo, bar)
.
{
"a": {
"b1": {
"c1": 42
},
"b2": {
"c2": 2
}
}
}
Since both foo
and bar
have a field a
, is is merged and the final results contains both b1
and b2
.
So to reiterate, +
is a "flat" operation which overrides the fields of the first object with the fields of the second object.
This is not the end of the story, though. You mentioned field+: value
syntax and I will try to explain what it really does. In Jsonnet +
is not just overwriting, but inheritance in OO sense. It creates an object which is the result of the second object inheriting from the first one. It's a bit exotic to have an operator for that – in all mainstream languages such relationships are statically defined. In Jsonnet, when you do foo + bar
, the bar
object has access to stuff from foo
through super
:
{ a: 2 } + { a_plus_1: super.a + 1}
This results in:
{
"a": 2,
"a_plus_1": 3
}
You can use this feature to merge the fields deeper inside:
{ a: {b: {c1: 1}, d: 1}} +
{ a: super.a + {b: {c2: 2} } }
Resulting in:
{
"a": {
"b": {
"c2": 2
},
"d": 1
}
}
This is a bit repetitive, though (it would be annoying if the field name was longer). So we have a nice syntax sugar for that:
{ a: {b: {c1: 1} , d: 1}} +
{ a+: {b: {c2: 2}} }
Please note that in these examples we only did the merging for one particular field we chose. We still replaced the value of a.b
. This is much more flexible, because in many cases you can't just naively merge all stuff inside (sometimes a nested object is "atomic" and should be replaced completely).
The version in +:
works in the same way as the version with super
. The subtle difference is that +:
actually translates to something like if field in super then super.field + val else val
, so it also returns the same value when super
is not provided at all or doesn't have this particular field. For example {a +: {b: 42}}
evaluates just fine to {a: { b: 42 }}
.
Mandatory sermon: while +
is very powerful, please don't abuse it. Consider using using functions instead of inheritance when you need to parameterize something.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With