jak*_*bka 4 python yaml pyyaml ruamel.yaml
我们需要解析包含重复密钥的YAML文件,而所有这些都需要解析。跳过重复项是不够的。我知道这违反了YAML规范,我不想这样做,但是我们使用的第三方工具可以启用此用法,因此我们需要对其进行处理。
文件示例:
build:
step: 'step1'
build:
step: 'step2'
Run Code Online (Sandbox Code Playgroud)
解析之后,我们应该具有与此类似的数据结构:
yaml.load('file.yml')
# [('build', [('step', 'step1')]), ('build', [('step', 'step2')])]
Run Code Online (Sandbox Code Playgroud)
dict 不能再用于表示已解析的内容。
我正在寻找Python中的解决方案,但没有找到支持此功能的库,是否错过了任何内容?
另外,我很乐于写自己的东西,但想使其尽可能简单。ruamel.yaml看起来像Python中最先进的YAML解析器,并且看起来具有一定程度的可扩展性,是否可以扩展以支持重复字段?
PyYAML只会默默覆盖第一个条目,ruamel.yaml¹将为DuplicateKeyFutureWarningif(如果与旧版API一起使用),并DuplicateKeyError为新API 提出一个。
如果您不想Constructor为所有类型创建一个完整的类型,SafeConstructor则应重写in中的映射构造函数:
import sys
from ruamel.yaml import YAML
from ruamel.yaml.constructor import SafeConstructor
yaml_str = """\
build:
step: 'step1'
build:
step: 'step2'
"""
def construct_yaml_map(self, node):
# test if there are duplicate node keys
data = []
yield data
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=True)
val = self.construct_object(value_node, deep=True)
data.append((key, val))
SafeConstructor.add_constructor(u'tag:yaml.org,2002:map', construct_yaml_map)
yaml = YAML(typ='safe')
data = yaml.load(yaml_str)
print(data)
Run Code Online (Sandbox Code Playgroud)
这使:
[('build', [('step', 'step1')]), ('build', [('step', 'step2')])]
Run Code Online (Sandbox Code Playgroud)
但是,似乎没有必要将step: 'step1'其列入列表。以下仅在有重复项的情况下创建列表(可以通过缓存的结果进行优化,如有必要self.construct_object(key_node, deep=True)):
def construct_yaml_map(self, node):
# test if there are duplicate node keys
keys = set()
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=True)
if key in keys:
break
keys.add(key)
else:
data = {} # type: Dict[Any, Any]
yield data
value = self.construct_mapping(node)
data.update(value)
return
data = []
yield data
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=True)
val = self.construct_object(value_node, deep=True)
data.append((key, val))
Run Code Online (Sandbox Code Playgroud)
这使:
[('build', {'step': 'step1'}), ('build', {'step': 'step2'})]
Run Code Online (Sandbox Code Playgroud)
一些要点:
<<: *xyz)yaml = YAML()),则需要更复杂的功能construct_yaml_map。如果要转储输出,则应YAML()为此实例一个新实例,而不是重新使用用于加载的“已修补”实例(这可能会起作用,这是可以肯定的):
yaml_out = YAML(typ='safe')
yaml_out.dump(data, sys.stdout)
Run Code Online (Sandbox Code Playgroud)
给出(与第一个construct_yaml_map):
- - build
- - [step, step1]
- - build
- - [step, step2]
Run Code Online (Sandbox Code Playgroud)在PyYAML或ruamel.yaml中不起作用的是yaml.load('file.yml')。如果您不想open()自己归档文件,则可以执行以下操作:
from pathlib import Path # or: from ruamel.std.pathlib import Path
yaml = YAML(typ='safe')
yaml.load(Path('file.yml')
Run Code Online (Sandbox Code Playgroud)¹ 免责声明:我是该软件包的作者。
您可以覆盖 pyyaml 加载密钥的方式。例如,您可以使用 defaultdict 以及每个键的值列表:
from collections import defaultdict
import yaml
def parse_preserving_duplicates(src):
# We deliberately define a fresh class inside the function,
# because add_constructor is a class method and we don't want to
# mutate pyyaml classes.
class PreserveDuplicatesLoader(yaml.loader.Loader):
pass
def map_constructor(loader, node, deep=False):
"""Walk the mapping, recording any duplicate keys.
"""
mapping = defaultdict(list)
for key_node, value_node in node.value:
key = loader.construct_object(key_node, deep=deep)
value = loader.construct_object(value_node, deep=deep)
mapping[key].append(value)
return mapping
PreserveDuplicatesLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, map_constructor)
return yaml.load(src, PreserveDuplicatesLoader)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6241 次 |
| 最近记录: |