如何为ruamel.yaml创建自定义yaml映射转储程序? [英] How to create a custom yaml mapping dumper for ruamel.yaml?
问题描述
我正在尝试为某些配置对象创建自定义的YAML转储器/加载器.为了简单起见,假设我们要将类Hero
的对象转储到hero.yml
文件.
I'm trying to make a custom YAML dumper/loader for some configuration objects. For simplicity, assuming we want to dump a object of class Hero
to a hero.yml
file.
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
然后通过ruamel.yaml
yaml.register_class(Hero)
并尝试转储并加载:
h = Hero('Saber', 15)
with open('config.yml', 'w') as fout:
yaml.dump(h, fout)
with open('config.yml') as fin:
yaml.load(fin)
它完美地工作!
但是,当我需要更灵活的行为时,因此必须使用自定义的from_yaml
和to_yaml
方法,这是有问题的.
However, when I need a more flexible behavior, thus a custom from_yaml
and to_yaml
method is necessary, there is problem.
Hero
的实现更改为:
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def to_yaml(cls, representer, data):
return representer.represent_mapping(cls.yaml_tag,
{'name': data.name, 'age': data.age})
@classmethod
def from_yaml(cls, constructor, node):
print(node) # for debug
value = constructor.construct_mapping(node)
return cls(**value)
自卸车工作正常.但是加载失败,无法加载YAML文件.一个 引发异常:
The dumper works just as desired. But the load failed to load the YAML file. An Exception is thrown:
243 def check_mapping_key(self, node, key_node, mapping, key, value):
244 # type: (Any, Any, Any, Any, Any) -> None
--> 245 if key in mapping:
246 if not self.allow_duplicate_keys:
247 args = [
TypeError: argument of type 'NoneType' is not iterable
在标有for debug
的print(node)
行中,加载的节点为:
By the print(node)
line marked with for debug
, the node loaded is:
MappingNode(tag='!Hero', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='name'), ScalarNode(tag='tag:yaml.org,2002:str', value='Saber')), (ScalarNode(tag='tag:yaml.org,2002:str', value='age'), ScalarNode(tag='tag:yaml.org,2002:int', value='15'))])
不使用默认转储程序/加载程序的原因
此示例是显示问题的最小情况,在实际情况下,我正在尝试仅转储对象的一部分,例如
Reason of not using default dumper/loader
This sample is a minimal case to show the problem, in real case, I'm trying to dump only part of the object, like
class A:
yaml_tag = '!A'
def __init__(self, name, age):
self.data = {'name': name, 'age': age}
所需的A('Saber', 15)
的YAML文件是
The desired YAML file of A('Saber', 15)
is
!A
name: Saber
age: 15
在这种情况下,我不知道如何使默认的自卸车/装载机正常工作.
I do not know how to make the default dumper/loader work in this case.
我的错误在哪里导致失败?如何解决这个问题呢?
Where is my mistake that makes this failed? How to solve this problem?
推荐答案
RoundTripConstructor.construct_mapping
的定义是::
def construct_mapping(self, node, maptyp=None, deep=False)
,它需要知道预期要进行哪种映射
构造.在RoundTripDumper中有一些期望
可以附加到这样的对象上,因此您最好模拟一下
RoundTripDumper传递中的例程:CommentedMap
(正常
dict
不起作用).
and it needs to know what kind of mapping it is expected to
construct. There is some expectation in the RoundTripDumper on what
can be attached to such an object, so you best of emulating what the
routines in the RoundTripDumper pass: CommentedMap
(a normal
dict
is not going to work).
因此,您需要执行以下操作:
So you will need to do something like:
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
yaml = YAML()
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def to_yaml(cls, representer, data):
return representer.represent_mapping(cls.yaml_tag,
{'name': data.name, 'age': data.age})
@classmethod
def from_yaml(cls, constructor, node):
data = CommentedMap()
constructor.construct_mapping(node, data, deep=True)
return cls(**data)
def __str__(self):
return "Hero(name -> {}, age -> {})".format(self.name, self.age)
yaml.register_class(Hero)
ante_hero = Hero('Saber', 15)
with open('config.yml', 'w') as fout:
yaml.dump(ante_hero, fout)
with open('config.yml') as fin:
post_hero = yaml.load(fin)
print(post_hero)
给出:
Hero(name -> Saber, age -> 15)
以上方法之所以有效,是因为您的班级相对简单, 递归部分,您需要遵循两步创建过程, 具有创建的对象的初始产量,以便可以在递归期间使用它.
The above works because your class is relatively simple, if it could have recursive parts, you would need to follow a two-step creation process, with initial yield of the object created, so that it can be used during the recursion.
maptyp
默认为None
是历史记录,必须为
放.例如. construct_mapping
要做的第一件事是
尝试附加注释(如果节点上有注释).
我将在0.15.55中删除默认值,这样会更明智
如果您将其遗漏,则会出现错误.
That the maptyp
defaults to None
is historical, it must be
set. E.g. one of the first things that construct_mapping
does is
trying to attach comments (if any were available on the node).
I'll remove the default value in 0.15.55, which gives a more sensible
error if you leave it out, like you did.
这篇关于如何为ruamel.yaml创建自定义yaml映射转储程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!