如何为ruamel.yaml创建自定义yaml映射转储程序?

时间:2017-10-18 09:28:47

标签: python yaml ruamel.yaml

我正在尝试为某些配置对象创建自定义YAML转储器/加载器。为简单起见,假设我们要将类CollectivePerson [id=1, addresses=[Address 1, Address 4], phones=[Phone 1, Phone 4]] CollectivePerson [id=2, addresses=[Address 2, Address 6, Address 5], phones=[Phone 5, Phone 2, Phone 6]] CollectivePerson [id=3, addresses=[Address 3], phones=[Phone 3]] 的对象转储到Hero文件。

使用默认转储程序/加载程序

的示例
hero.yml

然后按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) to_yaml方法失败

但是,当我需要更灵活的行为,因此需要自定义from_yamlfrom_yaml方法时,就会出现问题。

to_yaml的实施更改为:

Hero

翻斗车的工作方式与预期一致。但是加载无法加载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):
        print(node) # for debug
        value = constructor.construct_mapping(node)
        return cls(**value)

通过标有 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 的{​​{1}}行,加载的节点为:

print(node)

不使用默认转储程序/加载程序的原因

此示例是显示问题的最小案例,在实际情况下,我试图仅转储部分对象,例如

for debug

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'))]) 所需的YAML文件是

class A:
    yaml_tag = '!A'
    def __init__(self, name, age):
        self.data = {'name': name, 'age': age}

我不知道在这种情况下如何使默认的转储器/加载器工作。

我的错误在哪里导致失败?如何解决这个问题呢?

1 个答案:

答案 0 :(得分:0)

pandas 0.22.0的定义是::

RoundTripConstructor.construct_mapping

,它需要知道期望进行哪种映射 构造。在RoundTripDumper中有一些期望 可以附加到这样的对象上,因此您最好模拟一下 RoundTripDumper传递中的例程:def construct_mapping(self, node, maptyp=None, deep=False) (正常 CommentedMap无法正常工作。

因此,您需要执行以下操作:

dict

给出:

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) 默认为maptyp是历史的,它必须是 组。例如。 None做的第一件事是 尝试附加注释(如果该节点上有注释)。 我将在0.15.55中删除默认值,这样会更明智 如果您将其遗漏,则会出现错误。