如何使用 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 标签,
加载方式不同。所以
如果你需要更新这些文件,那么你应该注册 类(让我知道
如果那不成功)。
递归遍历数据结构并重写标签以弥补此错误
将无法工作,因为 <>
在转储时被转义。
我有一个这样的 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 标签,
加载方式不同。所以
如果你需要更新这些文件,那么你应该注册 类(让我知道
如果那不成功)。
递归遍历数据结构并重写标签以弥补此错误
将无法工作,因为 <>
在转储时被转义。