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

使用Python在YAML中保留锚点和别名,python,yaml,cross-reference,ruamel.yaml,Python,Yaml,Cross Reference,Ruamel.yaml,我正在用Python编辑一个带有大量锚和别名的大型YAML文档。我希望能够根据它引用的节点的数据来确定锚点是如何派生的 例如,节点有一个“name”字段,我希望锚点是该字段的值,而不是随机id号 Pyaml或ruamel.yaml是否可能实现这一点?有几点需要记住: YAML没有字段。我假设这是您对映射中键的解释,因此您希望与映射关联的锚点与键“name”的值相同 在加载期间,遇到锚点时创建的事件不知道它是标量、序列还是映射上的锚点。更不用说它可以访问“name”的值了 在加载期间更改锚点很棘

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

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


Pyaml或ruamel.yaml是否可能实现这一点?

有几点需要记住:

  • YAML没有字段。我假设这是您对映射中键的解释,因此您希望与映射关联的锚点与键“
    name
    ”的值相同
  • 在加载期间,遇到锚点时创建的事件不知道它是标量、序列还是映射上的锚点。更不用说它可以访问“
    name
    ”的值了
  • 在加载期间更改锚点很棘手,因为您必须跟踪引用原始锚点的别名(并将其映射到其新值)
  • 在PyYAML中,锚点名称是在
    dump
    -ing过程中创建的,因此在使用PyYAML时必须钩住锚点名称。您也可以使用
    ruamel.yaml
  • 只有
    ruamel.yaml
    能够在往返途中保留锚。也就是说,即使键“
    name
    ”的值发生变化(假设您在默认生成的表单
    idNNNN
    上进行测试),您也可以使锚保持不变
使用
ruamel.yaml
时,可以递归遍历数据结构,跟踪已访问的节点(如果子节点包含祖先),当遇到
ruamel.yaml.comments.CommentedMap
时,设置锚点(当前属性的值为
ruamel.yaml.comments.Anchor.attrib
\u yaml\u Anchor
)。未测试代码:

if isinstance(x, ruamel.yaml.comments.CommentedMap):
    if 'name' in x:
        x.yaml_set_anchor(x['name'])
如果您有一个YAML文档可以往返,您可以挂接到representer:

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
s实际上都是位于引擎盖下的
CommentedMap
s(即,您添加了normal
dict
s,然后取消对进行转换的两行的注释。

锚点是如何派生的,这是什么意思?转储数据结构时,PyYAML和ruamel.yaml检查特定的复杂对象(即不是像整数、字符串这样的原始对象)已转储并创建别名(如果已转储)。ruamel.yaml所做的一件事是将原始锚点名称附加到该复杂结构,以便它可以重用它。但是没有引用列表,有一个对象具有通过数据结构到该对象的多条路径。您必须遍历树才能找到所有引用就像翻车机一样。谢谢你的解释。我希望能够在初始负载上修改锚。这只有通过查看事件级别才能实现吗?谢谢你,这是一个非常详细的答案,有助于了解我想要的东西是否可行。也感谢你在ruamel.yaml上的工作。