我正在设计一个YAML模板,该模板将允许使用特定的!Tags
。在python中加载YAML文件时处理标签的方法是添加一个构造函数:yaml.add_constructor
。我想添加一个ScalarNode构造函数,但返回值应基于作为被评估节点的同级出现的数据。
def sibling_constructor(loader, node):
value = loader.construct_scalar(node)
# How to get parent node's child nodes?
return value + ' are dangerous.'
yaml.add_constructor('!Sibling', sibling_constructor)
return yaml.load(myFile)
Yaml:
Thing:
ParentThing:
Child1: Fast cars
Child2: Slow cars
Child3: !Sibling Child1
处理完YAML文件后,我希望内存字典中会出现如下所示:
{
'Thing': {
'ParentThing': {
'Child1': 'Fast cars',
'Child2': 'Slow cars',
'Child3': 'Fast cars are dangerous.'
}
}
}
YAML模块内是否有获取父节点的功能?如果没有,我将如何遍历字典以获取父节点和兄弟节点?
答案 0 :(得分:0)
YAML模块中没有功能可访问同级或
sibling_constructor
中的父项,因为必须通过
参数loader
或node
。
node
参数是一个Node
实例,
attributes:tag
,
value
,start_mark
,end_mark
,comment
,anchor
不包含上下文信息(mark
包含指向源的指针
流中,comment
可能在该行上添加了行尾注释。
loader
参数没有此信息要少得多
很清楚,因为它的数据结构要复杂得多。但是你可能会
没意识到是施工过程是深度的
首先,所以您的父母尚未建造,因此
在处理完最后一个节点之前,没有从YAML加载的数据结构的入口点,
之后可以完全创建其父级,然后是其父级
等等,直到递归结束并且构造的数据从
load()
函数。
但并非所有希望都消失了,如果您使用默认的往返加载程序,则可以访问
标记,而无需添加构造函数,因此您可以轻松地进行后处理过程。
假设您的输入在文件input.yaml
中:
import sys
from pprint import pprint
from pathlib import Path
import ruamel.yaml
input_path = Path('input.yaml')
def sibling(d):
# recurse into a data structure loaded from YAML
if isinstance(d, dict):
for k, v in d.items():
try:
if v._yaml_tag.value == '!Sibling':
d[k] = d[v.value] + ' are dangerous.'
except (AttributeError, ):
pass
sibling(v)
elif isinstance(d, list):
for item in d:
sibling(item)
yaml = ruamel.yaml.YAML()
data = yaml.load(input_path)
sibling(data)
pprint(data)
print('-------')
yaml.dump(data, sys.stdout)
给予:
{'Thing': {'ParentThing': {'Child1': 'Fast cars',
'Child2': 'Slow cars',
'Child3': 'Fast cars are dangerous.'}}}
-------
Thing:
ParentThing:
Child1: Fast cars
Child2: Slow cars
Child3: Fast cars are dangerous.
虽然可以注册构造一个新方法的新方法
映射(construct_yaml_map
),并附加dict
构造为Constructor
实例,但是这个dict
一次更新所有映射的键/值对
解决:
def construct_yaml_map(self, node):
data = self.yaml_base_dict_type() # type: Dict[Any, Any]
self._tw_current_dict = data
yield data
value = self.construct_mapping(node)
data.update(value)
ruamel.yaml.SafeConstructor.add_constructor(u'tag:yaml.org,2002:map', construct_yaml_map)
yaml.add_constructor('!Sibling', sibling_constructor, constructor=ruamel.yaml.SafeConstructor)
return yaml.safe_load(myFile)
因此,当您将以上内容添加到代码中时,您的sibling_constructor
将被称为loader._tw_current_dict
,但为空。
(您应该使用SafeLoader
,没有必要使用从PyYAML继承的旧load
方法来使所有事情变得不安全)
您可能可以加入construct_mapping
,我曾尝试过
并陷入困境(我确实声称对YAML模块的源代码相当熟悉)。
您可能要考虑的一件事是将输入的YAML更改为:
Thing:
ParentThing:
Child1: &child Fast cars
Child2: Slow cars
Child3: !Sibling *child
即使用锚和别名的YAML中的常规引用机制。那个别名
*child
当然可以在创建Child3
的值时被解析。