我刚刚开始使用YAML(通过pyyaml),我想知道是否有任何方法可以说明密钥的值是密钥名称本身或父密钥。 例如
---
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
…
{‘foo’: {‘bar’: ‘bar’, ‘baz’: ’foo’}, ‘foo2’:{‘bar’:’bar’, ‘baz’:’foo2’}}
(分别注意bar和baz上的点和双点 - 这些只是获取键名和父键名称的占位符)
我尝试过使用add_constructor
:
def key_construct(loader, node):
# return the key here
pass
yaml.add_constructor('!.', key_construct)
但是Node没有持有密钥(或对父代的引用),我找不到获取它们的方法。
修改
所以,这是我的真实用例和基于Anthon响应的解决方案: 我有一个记录器配置文件(在yaml中),我想在那里重用一些定义:
handlers:
base: &base_handler
(): globals.TimedRotatingFileHandlerFactory
name: ../
when: midnight
backupCount: 14
level: DEBUG
formatter: generic
syslog:
class: logging.handlers.SysLogHandler
address: ['localhost', 514]
facility: local5
level: NOTSET
formatter: syslog
access:
<<: *base_handler
error:
<<: *base_handler
loggers:
base: &base_logger
handlers: [../, syslog]
qualname: ../
access:
<<: *base_logger
error:
<<: *base_logger
handlers: [../, syslog, email]
正如Anthon建议的那样,解决方案是在处理之后遍历配置字典:
def expand_yaml(val, parent=None, key=None, key1=None, key2=None):
if isinstance(val, str):
if val == './':
parent[key] = key1
elif val == '../':
parent[key] = key2
elif isinstance(val, dict):
for k, v in val.items():
expand_yaml(v, val, k, k, key1)
elif isinstance(val, list):
parent[key] = val[:] # support inheritance of the list (as in *base_logger)
for index, e in enumerate(parent[key]):
expand_yaml(e, parent[key], index, key1, key2)
return val
答案 0 :(得分:1)
构造元素时没有太多上下文,所以你不会在没有挖掘上下文的调用堆栈的情况下填写值来找到你的键,当然也不是父键。 loader
了解foo
,bar
和baz
,但不能用于确定哪个是相应的键或parent_key。
我建议您创建一个使用key_construct
返回的特殊节点,然后在YAML加载后,遍历yaml.load()
返回的结构。除非你有其他!
个对象,这使得最终组合的行为比序列/列表和映射/词组的纯组合更难:
import ruamel.yaml as yaml
yaml_str = """\
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
"""
class Expander:
def __init__(self, tag):
self.tag = tag
def expand(self, key, parent_key):
if self.tag == '!.':
return key
elif self.tag == '!..':
return parent_key
raise NotImplementedError
def __repr__(self):
return "E({})".format(self.tag)
def expand(d, key=None, parent_key=None):
if isinstance(d, list):
for elem in d:
expand(elem, key=key, parent_key=parent_key)
elif isinstance(d, dict):
for k in d:
v = d[k]
if isinstance(v, Expander):
d[k] = v.expand(k, parent_key)
expand(d[k], key, parent_key=k)
return d
def key_construct(loader, node):
return Expander(node.tag)
yaml.add_constructor('!.', key_construct)
yaml.add_constructor('!..', key_construct)
data = yaml.load(yaml_str)
print(data)
print(expand(data))
给你:
{'foo': {'bar': E(!.), 'baz': E(!..)}, 'foo2': {'bar': E(!.), 'baz': E(!..)}}
{'foo': {'bar': 'bar', 'baz': 'foo'}, 'foo2': {'bar': 'bar', 'baz': 'foo2'}}
¹这是使用ruamel.yaml完成的,我是作者。其中ruamel.yaml是一个功能超集的PyYAML应该可以正常工作。