如何使用 TAG 解析 YAML?

How can I parse YAML with TAGs?

我有一个这样的 YAML 文档:

 steps:
  - !<!entry>
    id: Entry-1
    actions: []
  - !<!replybuttons>
    id: ReplyButtons-langcheck
    footer: ''
  - !<!input>
    id: Input-langcheck
    var: Input-1
  - !<!logic>
    id: LangCheck-Logic
    entries:
      - condition: !<!equals>
          var: Input-langcheck
          isCaseSensitive: false

然后我尝试阅读它:

import yaml

yaml.safe_load(yaml_text)

但是我有一个错误:

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!entry'

如何解析带有此类标签的 YAML?

这个选项也不起作用。

def construct_entry(loader, node):
    value = loader.construct_scalar(node)
    return value

yaml.SafeLoader.add_constructor('!<!entry>', construct_entry)
result = yaml.safe_load(yaml_text)

如果我尝试使用 ruamel.yaml 我可以阅读 YAML 文档,但我仍然不明白我如何知道 python 数据中的标签。

import sys
from ruamel.yaml import YAML


class Entry:
    yaml_tag = '!<!entry>'

    def __init__(self, value, style=None):
        self.value = value
        self.style = style

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(cls.yaml_tag,
                                            u'{.value}'.format(node), node.style)

    @classmethod
    def from_yaml(cls, constructor, node):
        return cls(node.value, node.style)


yaml_text = """\
steps:
  - !<!entry>
    id: 1
    action: 2
  - !<!entry>
    id: 2
    action: 3
"""


yaml1 = YAML(typ='rt')

data1 = yaml1.load(yaml_text)

print(f'{data1=}')
yaml1.dump(data1, sys.stdout)

yaml2 = YAML(typ='rt')
yaml2.register_class(Entry)

data2 = yaml2.load(yaml_text)

print(f'{data2=}')
yaml1.dump(data2, sys.stdout)

效果完全一样

data1=ordereddict([('steps', [ordereddict([('id', 1), ('action', 2)]), ordereddict([('id', 2), ('action', 3)])])])
steps:
- !entry
  id: 1
  action: 2
- !entry
  id: 2
  action: 3
data2=ordereddict([('steps', [ordereddict([('id', 1), ('action', 2)]), ordereddict([('id', 2), ('action', 3)])])])
steps:
- !entry
  id: 1
  action: 2
- !entry
  id: 2
  action: 3

如果只需要检查标签 和 ,对应加载 dict 和 list sub类 保留 他们在 .tag 属性中的标记(这可能会更改,因此请固定您使用的 ruamel.yaml 版本):

import sys
import ruamel.yaml

yaml_str = """\
steps:
- !<!entry>
  id: Entry-1
  actions: []
- !<!replybuttons>
  id: ReplyButtons-langcheck
  footer: ''
- !<!input>
  id: Input-langcheck
  var: Input-1
- !<!logic>
  id: LangCheck-Logic
  entries:
    - condition: !<!equals>
        var: Input-langcheck
        isCaseSensitive: false
"""
    
yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
print('id', data['steps'][1]['id'])
print('tag', data['steps'][1].tag.value)

给出:

id ReplyButtons-langcheck
tag !replybuttons

您的第一次尝试没有成功是因为您的标签很特殊,因为 <>,这些 是 verbatim tags,在这种情况下是必要的 允许以感叹号开头的标签。所以当 YAML 包含 !<abc> 您使用 add_constructor 注册 !abc(我认为您可以省略!),当您的 YAML 包含 !<!abc> 时,您注册 !abc。 解析器去除了这些逐字标记的 <>,这就是打印标记的原因 加载后不包含它们。

写这篇文章时我注意到 round-trip 解析器不检查标签是否需要 逐字记录。所以如果你转储加载的数据,你会得到 non-verbatim 标签, 加载方式不同。所以 如果你需要更新这些文件,那么你应该注册 类(让我知道 如果那不成功)。 递归遍历数据结构并重写标签以弥补此错误 将无法工作,因为 <> 在转储时被转义。