如何更改 ruamel.yaml 中对锚点的属性引用之一

How to change one of the attribute reference to anchor in ruamel.yaml

我在我的项目中使用 ruamel.yaml 并且有一个使用锚点的 YAML 文档,有多个地方引用了该锚点。我想在不触及其他地方的情况下更改引用锚点的地方之一的属性之一。

下面的代码详细展示了我想要做什么。

yaml = ruamel.yaml.YAML()
yaml_str = """\
foo: &foo
  color: red
  length: 599
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params: *foo
"""
data = yaml.load(yaml_str)
data["anotherbar"]["params"]["length"] = 39
yaml.dump(data, sys.stdout)

以上代码会输出

foo: &foo
  color: red
  length: 39
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params: *foo

我想更改“anotherbar”中的参数,但它也会更改“bar”。

如果我在分配新值之前复制参数,它会起作用,但它也会复制我不想更改的其他参数:

data["anotherbar"]["params"] = data["anotherbar"]["params"].copy()
data["anotherbar"]["params"]["length"] = 39
yaml.dump(data, sys.stdout)

输出:

foo: &foo
  color: red
  length: 599
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params:
    color: red
    length: 39
    weight: 32.3

但我实际上想要以下 YAML,没有任何重复:

foo: &foo
  color: red
  length: 599
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params:
    <<: *foo
    length: 39

如何以编程方式获取此合并密钥?

ruamel.yaml 可以往返你想要的输出:

import sys
import io
import ruamel.yaml

yaml_str = """\
foo: &foo
  color: red
  length: 599
  weight: 32.3

anotherbar:
  name: bar_another
  params:
    <<: *foo
    length: 39
"""

yaml = ruamel.yaml.YAML()
buf = io.StringIO()
data = yaml.load(yaml_str)
yaml.dump(data, buf)
assert buf.getvalue() == yaml_str

所以可以从你的 实际输入,但没有这样做的内置例程。

params = data["anotherbar"]["params"]
print(type(params))
print(params.merge)

给出:

<class 'ruamel.yaml.comments.CommentedMap'>
[(0, ordereddict([('color', 'red'), ('length', 599), ('weight', 32.3)]))]

那个“ordereddict”实际上是 data["foo"] 对象。

无需复制,您需要创建特殊的字典类型 ruamel.yaml 在内部使用,然后连接合并键并设置 key/value,并附上这个 在数据树中。

import sys
import ruamel.yaml

yaml_str = """\
foo: &foo
  color: red
  length: 599
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params: *foo
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)

upd = ruamel.yaml.comments.CommentedMap()  # empty dict like type
data["anotherbar"]["params"] = upd         # attach it to the data
upd.add_yaml_merge([(0, data["foo"])])     # attach original dict as merge
upd["length"] = 39                         # set the value you want
yaml.dump(data, sys.stdout)

这给了你想要的:

foo: &foo
  color: red
  length: 599
  weight: 32.3

bar:
  name: bar_one
  params: *foo

anotherbar:
  name: bar_another
  params:
    <<: *foo
    length: 39

add_yaml_merge是构造阶段使用的内部例程 加载。它可能会更改,恕不另行通知,因此请确保固定您的版本 ruamel.yaml 在生产环境中。