如何在PyYAML解析器中挂钩过滤器?

时间:2016-11-14 15:37:41

标签: python yaml pyyaml

我有一个我要解析的YAML文件。

出于多种原因,我想禁止在锚点中使用点.,只需在解析时将其替换为_

简单地说,我想离开这个:

foo:
    bar.baz:
        - egg
        - spam

到那个:

foo:
    bar_baz:
        - egg
        - spam

我知道这种转换可以在生成的Python字典上执行,但它不适合它:解析器应该抛出错误,或者它应该替换有问题的值。

我已经尝试将Loader子类化以进行此类转换,但是所有被覆盖的函数都没有任何效果。

1 个答案:

答案 0 :(得分:0)

没有简单的机制来替换每个映射键通过的钩子形式的键(无论如何,您可能希望拥有更多的上下文,而不仅仅是拥有键)。 有多种方法可以解决这个问题:

  • 您可以创建一个新的Loader,它将拥有您自己的Constructor子类,用于对映射键进行转换。这是IMO的正确解决方案,因为它不会影响其他YAML的加载。然而,这也是一个比较棘手的方法
  • 您可以为正在使用的Loader添加新的构造函数,从而覆盖现有的构造函数。如果您不做任何特殊操作,这将影响将来加载更多YAML文件。
  • 你可以包装现有的映射构造函数,加载你的YAML并移回原来的。这不会影响其他YAML文件的加载。

后者可以通过以下方式完成:

import sys
import ruamel.yaml

yaml_str = """\
foo:
    bar.baz:
        - egg
        - spam
"""


def alt_construct_mapping(self, *args, **kw):
    """replace keys with dot"""
    m = self.org_construct_mapping(*args, **kw)
    for k in m:
        if '.' in k:
            m[k.replace('.', '_')] = m.pop(k)
    return m

# backup up the constructor
ruamel.yaml.constructor.BaseConstructor.org_construct_mapping = \
    ruamel.yaml.constructor.BaseConstructor.construct_mapping

# replace the constructor
ruamel.yaml.constructor.BaseConstructor.construct_mapping = alt_construct_mapping


data = ruamel.yaml.safe_load(yaml_str)
ruamel.yaml.round_trip_dump(data, sys.stdout)

# put original constructor back
ruamel.yaml.constructor.BaseConstructor.construct_mapping = \
    ruamel.yaml.constructor.BaseConstructor.org_construct_mapping

给出:

foo:
  bar_baz:
  - egg
  - spam

这是使用ruamel.yaml完成的,这是PyYAML的增强版本,我是作者。对于PyYAML,只要您的YAML没有任何YAML版本1.2结构,将ruamel.yaml替换为yaml,将round_trip_load/dump替换为safe_load/dump