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
.
Итак, повторюсь, +
— это «плоская» операция, которая переопределяет поля первого объекта полями второго объекта.
Однако это не конец истории. Вы упомянули синтаксис field+: value
, и я попытаюсь объяснить, что он на самом деле делает. В Jsonnet +
— это не просто перезапись, а наследование в объектно-ориентированном смысле. Он создает объект, который является результатом наследования второго объекта от первого. Немного экзотично иметь для этого оператор — во всех основных языках такие отношения определены статически. В 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
. Это намного более гибко, потому что во многих случаях вы не можете просто наивно объединить все внутри (иногда вложенный объект является «атомарным» и должен быть полностью заменен).
Версия в +:
работает так же, как и версия с super
. Небольшое отличие состоит в том, что +:
на самом деле преобразуется в что-то вроде if field in super then super.field + val else val
, поэтому оно также возвращает то же значение, когда super
вообще не указано или не имеет этого конкретного поля. Например, {a +: {b: 42}}
отлично оценивается как {a: { b: 42 }}
.
Обязательная проповедь: несмотря на то, что +
очень мощное средство, не злоупотребляйте им. Рассмотрите возможность использования функций вместо наследования, когда вам нужно что-то параметризовать.
person
sbarzowski
schedule
07.05.2020