PyYAML 使用重复键加载 YAML 1.1
PyYAML loading YAML 1.1 with duplicate keys
我正在尝试使用 PyYAML 加载 YAML 1.1 文件(它们没有这样标记,但它们具有八进制整数值 0123 而不是 0o123).
我不知道这些文件是如何生成的,但问题之一是其中一些文件有重复的键,例如:
xxx:
aaa: 011
bbb: 012
ccc: 013
aaa: 014
我正在使用 yaml.safe_load()
加载这些文件。
阅读 YAML 文档第 10.2 节后,我预计会收到警告,并且 aaa
的值为 9:
It is an error for two equal keys to appear in the same mapping node.
In such a case the YAML processor may continue, ignoring the second
key: value pair and issuing an appropriate warning.
但我没有收到任何警告,并且值为 12。
这是一个错误吗?有没有办法让 PyYAML 获得 select 键的第一个值?
我查看了一些其他语言的库,以便在进一步处理之前清理它,但这些库要么抛出错误,要么继续使用第二个值。
有很多文件,通常重复项嵌套得更深。它们的键之间可以有复杂的结构,并且重复的键对于它们出现的映射也不是唯一的,这是有效的。使用 awk 修复此问题将无法处理这些文件。而且太多了,无法手动修复。
我会说这是 PyYAML 中的错误。违规代码是 here:
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.Hashable):
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unhashable key", key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
很明显,没有检查密钥是否存在。您必须对 Constructor
进行子类化才能使 construct_mapping()
包含支票:
if key in mapping:
warnings.warn(somewarning)
else:
mapping[key] = value
然后使用 Constructor
创建一个 Loader
。
使用起来可能更简单ruamel.yaml
(免责声明:我是作者
那个包裹)。假设您禁用 DuplicateKeyError
,它会正确加载它,
并明确将 YAML 1.1 设置为输入格式:
import sys
import ruamel.yaml
yaml_file = Path('xx.yaml')
yaml = ruamel.yaml.YAML()
yaml.version = (1, 1)
yaml.indent(mapping=3, sequence=2, offset=0)
yaml.allow_duplicate_keys = True
data = yaml.load(yaml_file)
assert data['xxx']['aaa'] == 9
yaml_out = ruamel.yaml.YAML()
yaml_out.dump(data, sys.stdout)
这给出:
xxx:
aaa: 9
bbb: 10
ccc: 11
您的八进制将被转换为小数(通常该信息是
保留,但在加载遗留 YAML 1.1 时不保留)。 PyYAML 将始终这样做。
我正在尝试使用 PyYAML 加载 YAML 1.1 文件(它们没有这样标记,但它们具有八进制整数值 0123 而不是 0o123).
我不知道这些文件是如何生成的,但问题之一是其中一些文件有重复的键,例如:
xxx:
aaa: 011
bbb: 012
ccc: 013
aaa: 014
我正在使用 yaml.safe_load()
加载这些文件。
阅读 YAML 文档第 10.2 节后,我预计会收到警告,并且 aaa
的值为 9:
It is an error for two equal keys to appear in the same mapping node. In such a case the YAML processor may continue, ignoring the second key: value pair and issuing an appropriate warning.
但我没有收到任何警告,并且值为 12。
这是一个错误吗?有没有办法让 PyYAML 获得 select 键的第一个值?
我查看了一些其他语言的库,以便在进一步处理之前清理它,但这些库要么抛出错误,要么继续使用第二个值。
有很多文件,通常重复项嵌套得更深。它们的键之间可以有复杂的结构,并且重复的键对于它们出现的映射也不是唯一的,这是有效的。使用 awk 修复此问题将无法处理这些文件。而且太多了,无法手动修复。
我会说这是 PyYAML 中的错误。违规代码是 here:
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.Hashable):
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unhashable key", key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
很明显,没有检查密钥是否存在。您必须对 Constructor
进行子类化才能使 construct_mapping()
包含支票:
if key in mapping:
warnings.warn(somewarning)
else:
mapping[key] = value
然后使用 Constructor
创建一个 Loader
。
使用起来可能更简单ruamel.yaml
(免责声明:我是作者
那个包裹)。假设您禁用 DuplicateKeyError
,它会正确加载它,
并明确将 YAML 1.1 设置为输入格式:
import sys
import ruamel.yaml
yaml_file = Path('xx.yaml')
yaml = ruamel.yaml.YAML()
yaml.version = (1, 1)
yaml.indent(mapping=3, sequence=2, offset=0)
yaml.allow_duplicate_keys = True
data = yaml.load(yaml_file)
assert data['xxx']['aaa'] == 9
yaml_out = ruamel.yaml.YAML()
yaml_out.dump(data, sys.stdout)
这给出:
xxx:
aaa: 9
bbb: 10
ccc: 11
您的八进制将被转换为小数(通常该信息是 保留,但在加载遗留 YAML 1.1 时不保留)。 PyYAML 将始终这样做。