如何在 jsonnet 中做正确的 for 循环?

how to do proper for-loops in jsonnet?

每次我以为我终于理解了jsonnet,它就来打我的脸...-.-

我有如下内容:

local applyModifications(kp) = {
  [topLvlKey]: {
    [subKey]: myfunction(kp[topLvlKey][subKey])
    for subKey in std.objectFieldsAll(kp[topLvlKey])
  },
  for topLvlKey in std.objectFieldsAll(kp)
};

我想遍历对象前 2 层内的所有内容并在那里应用一些函数...

基本上可行...但是取决于我使用 std.objectFieldsAll 还是 std.objectFields,隐藏字段之后是可见的还是完全丢失。

我如何would/could在不触及隐藏的“属性”的情况下做到这一点? 我知道我的问题是,我在这里使用对象理解并且(指错误消息)那些“对象理解不能有隐藏字段”...... 但据我了解 jsonnet,something-comprehensions 是创建 for 循环的唯一方法,对吧?

测试代码:

// vim: set ts=2 sw=2 expandtab :
local myfunction(o) = o {
  spec+: {
    foo: 'bar'
  }
};

local applyModifications(kp) = {
  [topLvlKey]: {
    [subKey]: myfunction(kp[topLvlKey][subKey])
    for subKey in std.objectFieldsAll(kp[topLvlKey])
  },
  for topLvlKey in std.objectFieldsAll(kp)
};    

local stack = {
  fooService: {
    fooResource: {
      kind: 'PrometheusRule',
      spec: {
        groups: [
          { name: 'fooGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
          { name: 'barGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
        ],
      },
    },
  },
  fooService2:: {
    fooResource: {
      kind: 'PrometheusRule',
      spec: {
        groups: [
          { name: 'fooGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
          { name: 'barGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
        ],
      },
    },
  },
};

local stack2 = applyModifications(stack);
   
{
  modified: stack2 
}

你可以通过继承来实现你想要的。

local applyModifications(obj, f) =
    obj + {
        [x] : f(obj[x]) for x in std.objectFieldsAll(obj)
    }
;

applyModifications({
    visible: "foo",
    hidden:: "bar",
}, function(x) x + " modified")

为清楚起见,这是单关卡,但创建两关卡版本应该很简单(如果您有任何问题,请告诉我)。

它起作用的原因是 : 是“默认可见性”,它采用它覆盖的字段的可见性。 (你也有带 :: 的强制可见字段)。

也就是说,您处于尴尬的境地,通常可以避免。 Jsonnet 中的对象替换了其他语言的对象(struct / class 实例)和映射(dict)。尽管这两个概念是统一的,但 OOP 特性并不总是能与数据结构特性很好地配合。

通常您希望将每个对象视为:

  • 一个数据对象,保持所有字段可见,避免self/super。您可以轻松地汇总处理这些字段。
  • OOP 对象或结构。您可以使用 selfsuper,隐藏字段等。您手动处理每个字段,因为每个字段都可以具有完全不同的含义和行为。

希望数据对象包含 OOP 对象,反之亦然。当一个物体处于中间地带时,尴尬就会随之而来。

But as far as I understand jsonnet, something-comprehensions are the only way to create for-loops, right?

感悟并不特殊。不要将它们视为命令式语言中的“for 循环”。数组理解基本上是 std.map 和 std.filter(具有任意嵌套)的语法糖。

按照上面的@sbarzowski 评论(我宁愿说的完整课程),顺便说一句,确保你使用的是 go-jsonnet fwiw(brew install go-jsonnet 在 macOS 上),你可以欺骗可见性通过将最后一行代码修改为“合并”:

[...]

local stack2 = applyModifications(stack);
   
{
  modified: stack + stack2 
}

@sbarzowski:

我花了一些时间来理解差异并采用我的示例的解决方案(2 个循环),但这是我得到的并且似乎有效:

local applyModifications(kp) = kp + {
  [topLvlKey]: kp[topLvlKey] + {
    [subKey]: myfunction(kp[topLvlKey][subKey])
    for subKey in std.objectFieldsAll(kp[topLvlKey])
  },
  for topLvlKey in std.objectFieldsAll(kp)
};

PS: 我正在使用 go jsonnet v0.17.0