yaml使用键或父键作为值

时间:2015-10-11 13:53:38

标签: yaml pyyaml

我刚刚开始使用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

1 个答案:

答案 0 :(得分:1)

构造元素时没有太多上下文,所以你不会在没有挖掘上下文的调用堆栈的情况下填写值来找到你的键,当然也不是父键。 loader了解foobarbaz,但不能用于确定哪个是相应的键或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应该可以正常工作。