2

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

例如,节点有一个“名称”字段,我希望锚点是该字段的值,而不是随机的 id 号。

PyYAML 或 ruamel.yaml 可以做到这一点吗?

4

1 回答 1

3

有几件事情要记住:

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

当您使用时,ruamel.yaml您可以递归地遍历数据结构,跟踪已经访问过的节点(如果子节点包含祖先),当遇到 a 时ruamel.yaml.comments.CommentedMap,设置锚点(当前值为ruamel.yaml.comments.Anchor.attribie的属性_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

但请注意,这假设您加载了数据并且所有dicts 实际上都是CommentedMaps 在引擎盖下。如果不是这种情况(即您添加了正常dict的 s,则取消注释进行转换的两行。

于 2018-02-01T09:45:56.027 回答