使用 Python 在 YAML 中保留锚点和别名

Preserving anchors and aliases in YAML with Python

我正在 Python 中编辑一个包含大量锚点和别名的大型 YAML 文档。我希望能够根据锚引用的节点中的数据确定锚是如何派生的。

例如,节点有一个 'name' 字段,我希望锚点是该字段的值而不是随机 ID 号。

PyYAML 或 ruamel.yaml 这可能吗?

有几点需要注意:

  • YAML 没有字段。我假设这是您对映射中键的解释,因此您希望与映射关联的锚点与键 'name'
  • 的值相同
  • 在加载期间,遇到锚点时创建的事件不知道它是标量、序列还是映射上的锚点。更不用说它可以访问“name”的值了。
  • 在加载期间更改锚点很棘手,因为您必须跟踪引用原始锚点的别名(并将它们映射到新值)
  • 在 PyYAML 中,锚点名称是在 dump-ing 期间创建的,因此您在使用 PyYAML 时必须挂接到它。你可以用 ruamel.yaml
  • 做同样的事情
  • 只有 ruamel.yaml 能够在往返过程中保留锚点。 IE。如果您可以使锚点持久化,即使键“name”的值发生变化(假设您在默认生成的表单 idNNNN
  • 上进行测试

当您使用 ruamel.yaml 时,您可以递归遍历数据结构,跟踪已经访问过的节点(以防子节点包含祖先),并在遇到 ruamel.yaml.comments.CommentedMap 时设置锚点(当前值为 ruamel.yaml.comments.Anchor.attrib 的属性,即 _yaml_anchor)。未经测试的代码:

if isinstance(x, ruamel.yaml.comments.CommentedMap):
    if 'name' in x:
        x.yaml_set_anchor(x['name'])

如果您有一个可以往返的 YAML 文档,您可以连接到表示器:

import sys
import ruamel.yaml
from ruamel.yaml.representer import RoundTripRepresenter

yaml_str = """\
# data = [dict(a=1, b=2, name='mydata'), dict(c=3)]
# data.append(data[0])
- &id001
  a: 1
  b: 2
  name: mydata
- c: 3
- *id001
"""

class MyRTR(RoundTripRepresenter):
    def represent_mapping(self, tag, mapping, flow_style=None):
        if 'name' in mapping:
            # if not isinstance(mapping, ruamel.yaml.comments.CommentedMap):
            #     mapping = ruamel.yaml.comments.CommentedMap(mapping)
        mapping.yaml_set_anchor(mapping['name'])

            mapping.yaml_set_anchor(mapping['name'])
        return RoundTripRepresenter.represent_mapping(
            self, tag, mapping, flow_style=flow_style)


yaml = ruamel.yaml.YAML()
yaml.Representer = MyRTR
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

给出:

# data = [dict(a=1, b=2, name='mydata'), dict(c=3)]
# data.append(data[0])
- &mydata a: 1
  b: 2
  name: mydata
- c: 3
- *mydata

但请注意,这假设您加载了数据并且所有 dict 实际上都是 CommentedMap 的。如果不是这种情况(即您添加了正常的 dicts,然后取消对进行转换的两行的注释。