我正在尝试使用PyYAML加载YAML 1.1文件(它们没有这样标记,但它们具有八进制整数值 0123 而不是 0o123 )。
我不知道这些文件是如何生成的,但是问题之一是其中一些文件具有重复的密钥,例如:
xxx:
aaa: 011
bbb: 012
ccc: 013
aaa: 014
我正在使用yaml.safe_load()
加载这些文件。
通过阅读YAML文档的10.2节,我希望得到一个警告,并且aaa
的值将为9:
两个相等的键出现在同一映射节点中是错误的。 在这种情况下,YAML处理器可能会继续运行,而忽略第二个 关键:值对并发出适当的警告。
但是我没有得到警告,该值为12。
这是一个错误吗?有没有办法让PyYAML为键选择第一个值?
我查看了一些其他语言的库,以在进一步处理之前对其进行清理,但是这些库确实抛出了错误,或者继续使用第二个值。
文件很多,通常重复项嵌套得更深。它们在键之间可能具有复杂的结构,并且重复的键也不是它们所在的映射唯一的,这是有效的。使用 awk 修复此问题将无法使用这些文件。还有太多的东西无法手工修复。
答案 0 :(得分:0)
我会说这是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
然后使用该Loader
创建一个Constructor
。
使用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会一直这样做。