解析YAML并假设某个路径始终为字符串

4 投票
2 回答
2804 浏览
提问于 2025-04-16 05:05

我正在使用来自 http://pyyaml.org 的 YAML 解析器,我希望它总是把某些字段当作字符串来处理,但我搞不清楚 add_path_resolver() 是怎么工作的。

举个例子:解析器会把 "version" 当作浮点数来处理:

network:
- name: apple
- name: orange
version: 2.3
site: banana

有些文件里有 "version: 2"(这被当作整数处理)或者 "version: 2.3 alpha"(这被当作字符串处理)。

我希望它们总是被当作字符串处理。

看起来 yaml.add_path_resolver() 应该可以让我指定,“当你看到 version: 时,总是把它当作字符串处理”,但这个功能的文档写得不太清楚。我的最佳猜测是:

yaml.add_path_resolver(u'!root', ['version'], kind=str)

但是这样并不奏效。

有没有什么建议可以让我确保 version 字段总是被当作字符串处理?

附注:这里有一些不同的 "version" 字符串及其被解析的方式:

(Pdb) import yaml
(Pdb) import pprint
(Pdb) pprint.pprint(yaml.load("---\nnetwork:\n- name: apple\n- name: orange\nversion: 2\nsite: banana"))
{'network': [{'name': 'apple'}, {'name': 'orange'}],
 'site': 'banana',
 'version': 2}
(Pdb) pprint.pprint(yaml.load("---\nnetwork:\n- name: apple\n- name: orange\nversion: 2.3\nsite: banana"))
{'network': [{'name': 'apple'}, {'name': 'orange'}],
 'site': 'banana',
 'version': 2.2999999999999998}
(Pdb) pprint.pprint(yaml.load("---\nnetwork:\n- name: apple\n- name: orange\nversion: 2.3 alpha\nsite: banana"))
{'network': [{'name': 'apple'}, {'name': 'orange'}],
 'site': 'banana',
 'version': '2.3 alpha'}

2 个回答

2

根据目前的源代码:

 # Note: `add_path_resolver` is experimental.  The API could be changed.

看起来这个还不完整(还没完成?)。我能想到的有效语法是:

yaml.add_path_resolver(u'tag:yaml.org,2002:str', ['version'], yaml.ScalarNode)

但是,它并没有按预期工作。

似乎系统首先会检查隐式类型解析器,如果有匹配的,就不会再检查用户自定义的解析器。想了解更多细节,可以查看resolver.py,找找resolve这个函数。

我建议你把version的内容改成:

version: !!str 2.3

这样做会确保它总是被转换成字符串。

1

到目前为止,最简单的解决办法就是不要使用基本的 .load() 方法(反正这个方法也不安全),而是用 Loader=BaseLoader,这样可以把每个标量值都当作字符串来加载:

import yaml

yaml_str = """\
network:
- name: apple
- name: orange
version: 2.3
old: 2
site: banana
"""

data = yaml.load(yaml_str, Loader=yaml.BaseLoader)
print(data)

结果是:

{'network': [{'name': 'apple'}, {'name': 'orange'}], 'version': '2.3', 'old': '2', 'site': 'banana'}

撰写回答