如何解决在ruamel from_yaml
我正在创建一个自定义的Yaml标签mytag。它可以包含任何给定的有效Yaml-映射,标量,锚点,序列等。
我如何实现mytag类来对该标签建模,以使ruamel解析!mytag
的内容与解析任何给定的Yaml完全相同? mytag
实例仅存储yaml内容的任何解析结果。
以下代码有效,并且断言应确切说明应该应该执行的操作,并且都可以通过。
但是我不确定它是否由于正确的原因而工作。 。 。特别是在from_yaml
类方法中,是否建议使用commented_obj = constructor.construct_undefined(node)
来实现这一点,并且消耗了1个生成器中只有1个正确吗?这不是偶然的吗?
我应该改用construct_object
还是construct_map
或。 。 。?我能够找到的示例倾向于知道其构造的类型,因此可以使用construct_map
或construct_sequence
来选择构造哪种类型的对象。在这种情况下,我实际上想将常规/标准ruamel解析带回去,以了解其中可能存在的未知类型,然后将其存储为自己的类型。
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap,CommentedSeq,TaggedScalar
class mytag():
yaml_tag = '!mytag'
def __init__(self,value):
self.value = value
@classmethod
def from_yaml(cls,constructor,node):
commented_obj = constructor.construct_undefined(node)
flag = False
for data in commented_obj:
if flag:
raise AssertionError('should only be 1 thing in generator??')
flag = True
return cls(data)
with open('mytag-sample.yaml') as yaml_file:
yaml_parser = ruamel.yaml.YAML()
yaml_parser.register_class(mytag)
yaml = yaml_parser.load(yaml_file)
custom_tag_with_list = yaml['root'][0]['arb']['k2']
assert type(custom_tag_with_list) is mytag
assert type(custom_tag_with_list.value) is CommentedSeq
print(custom_tag_with_list.value)
standard_list = yaml['root'][0]['arb']['k3']
assert type(standard_list) is CommentedSeq
assert standard_list == custom_tag_with_list.value
custom_tag_with_map = yaml['root'][1]['arb']
assert type(custom_tag_with_map) is mytag
assert type(custom_tag_with_map.value) is CommentedMap
print(custom_tag_with_map.value)
standard_map = yaml['root'][1]['arb_no_tag']
assert type(standard_map) is CommentedMap
assert standard_map == custom_tag_with_map.value
custom_tag_scalar = yaml['root'][2]
assert type(custom_tag_scalar) is mytag
assert type(custom_tag_scalar.value) is TaggedScalar
standard_tag_scalar = yaml['root'][3]
assert type(standard_tag_scalar) is str
assert standard_tag_scalar == str(custom_tag_scalar.value)
还有一些样本yaml:
root:
- item: blah
arb:
k1: v1
k2: !mytag
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
k3:
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
- item: argh
arb: !mytag
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
arb_no_tag:
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
- !mytag plain scalar
- plain scalar
- item: no comment
arb:
- one1
- two2
解决方法
在YAML中,您可以具有锚点和别名,并且让对象成为其自身的子对象(使用别名)是完全可以的。如果要转储Python数据结构data
:
data = [1,2,4,dict(a=42)]
data[3]['b'] = data
它转储到:
&id001
- 1
- 2
- 4
- a: 42
b: *id001
,为此,锚和别名是必需的。
在加载这样的结构时,ruamel.yaml会递归到嵌套的数据结构中,但是如果顶级节点尚未导致要构造可作为锚点引用的实际对象,则递归叶无法解析别名
为解决这个问题,除了标量值外,使用了一个生成器。它首先创建一个空对象,然后递归并更新其值。在调用构造函数的代码中,进行检查以查看是否返回了生成器,在这种情况下,对数据执行了next()
,并潜在地实现了“递归”自递归。
由于您调用construct_undefined()
,所以总会得到一个生成器。实际上,如果该方法检测到标量节点(当然不能递归),则可能会返回值,但不会。如果可以,您的代码将无法加载以下YAML文档:
!mytag 1
无需修改即可测试您是否生成了生成器,就像ruamel.yaml中的代码调用了各种构造函数那样,以便它可以处理construct_undefined
和e.g. construct_yaml_int
(不是生成器)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。