簡體   English   中英

如何為ruamel.yaml創建自定義yaml映射轉儲程序?

[英]How to create a custom yaml mapping dumper for ruamel.yaml?

我正在嘗試為某些配置對象創建自定義的YAML轉儲器/加載器。 為簡單起見,假設我們想將Hero類的對象轉儲到hero.yml文件中。

該示例適用於默認的自卸車/裝載機

class Hero:
    yaml_tag = '!Hero'
    def __init__(self, name, age):
        self.name = name
        self.age = age

然后通過ruamel.yaml添加默認的加載程序/ 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_yamlfrom_yaml方法失敗

但是,當我需要更靈活的行為,因此需要自定義from_yamlto_yaml方法時,就會出現問題。

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文件。 拋出異常:

    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 debugprint(node)行,加載的節點為:

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'))])

不使用默認轉儲程序/加載程序的原因

此示例是顯示問題的最小案例,在實際情況下,我試圖僅轉儲部分對象,例如

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

所需的A('Saber', 15) YAML文件是

!A
name: Saber
age: 15

在這種情況下,我不知道如何使默認的自卸車/裝載機正常工作。

我的錯誤在哪里導致失敗? 如何解決這個問題呢?

RoundTripConstructor.construct_mapping的定義是:

def construct_mapping(self, node, maptyp=None, deep=False)

並且它需要知道預期要構建哪種映射。 RoundTripDumper對可以附加到此類對象的東西有一些期望,因此您最好模擬RoundTripDumper傳遞的例程: 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是歷史記錄,必須進行設置。 例如, construct_mapping要做的第一件事就是嘗試附加注釋(如果節點上有注釋)。 我將在0.15.55中刪除默認值,如果您將其遺漏,將產生更明智的錯誤,就像您所做的那樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM