[英]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_yaml
和from_yaml
方法失敗 但是,當我需要更靈活的行為,因此需要自定義from_yaml
和to_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 debug
的print(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.