如何在 PyYAML 解析器中挂接过滤器?
How can I hook a filter in PyYAML parser?
我有一个要解析的 YAML 文件。
出于多种原因,我想禁止在锚 或 中使用点 .
,只需在解析时将其替换为 _
。
简单地说,我想从这里开始:
foo:
bar.baz:
- egg
- spam
到那个:
foo:
bar_baz:
- egg
- spam
我知道可以在生成的 Python 字典上执行这种转换,但它不是正确的位置:解析器应该抛出错误或者应该替换有问题的值。
我已经尝试子类化 Loader
以进行这种转换,但是 none 的重写函数似乎没有任何效果。
没有简单的机制来以某种挂钩的形式替换键,每个映射键都通过该挂钩传递(无论如何,您可能希望拥有比仅拥有键更多的上下文)。
有多种解决方法:
- 您可以创建一个新的
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
我有一个要解析的 YAML 文件。
出于多种原因,我想禁止在锚 或 中使用点 .
,只需在解析时将其替换为 _
。
简单地说,我想从这里开始:
foo:
bar.baz:
- egg
- spam
到那个:
foo:
bar_baz:
- egg
- spam
我知道可以在生成的 Python 字典上执行这种转换,但它不是正确的位置:解析器应该抛出错误或者应该替换有问题的值。
我已经尝试子类化 Loader
以进行这种转换,但是 none 的重写函数似乎没有任何效果。
没有简单的机制来以某种挂钩的形式替换键,每个映射键都通过该挂钩传递(无论如何,您可能希望拥有比仅拥有键更多的上下文)。 有多种解决方法:
- 您可以创建一个新的
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