python yaml 包在不需要时解析新行
python yaml package parsing new line when not needed
我已经搜索了几天,试图找出为什么我的 yaml 解析器(使用 PyYaml)没有像原始状态那样保存回 YAML。
YAML 中的原始行是:
healthcheck:
test: ["CMD-SHELL", "[ x\"`curl -k --silent -w '%{http_code}' https://localhost:4433 | grep 401`\" = x\"\" ] && exit 1 || exit 0"]
interval: 30s
但是新行(只是加载文件并再次保存):
healthcheck:
interval: 30s
test:
- CMD-SHELL
- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
= x"" ] && exit 1 || exit 0'
这里有两个问题:
1) "test" 值变成一个列表而不是 1 行键值对。
2)这里实际上有3个换行,
a) -CMD-SHELL
b)- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
c)= x"" ] && exit 1 || exit 0'
那么另一个问题是,为什么第三行是从第二行断的? (如果我显示白色 space,你会看到在第二行的末尾有 LF
然后开始第三行
PyYAML 不太擅长在往返过程中保留样式(加载、修改、安全),它实际上无法通过使用其 load/dump 参数按原样保留您的输入。为此,您需要修改 PyYAML 解析器。
这就是 ruamel.yaml
所做的(免责声明:我是该包的作者),它是专门为支持此类程序化往返(包括保留评论)而开发的:
import sys
import ruamel.yaml
from pathlib import Path
yaml_file = Path('test.yaml')
out_file = Path('out.yaml')
yaml = ruamel.yaml.YAML()
yaml.width = 2048
yaml.preserve_quotes = True
data = yaml.load(yaml_file)
yaml.dump(data, out_file)
这为您提供了一个 out.yaml
,其内容与 test.yaml
.
完全相同
默认宽度 (80) 会像在 PyYAML 中那样换行,因此将其设置为比最大长度行更长的值。 preserve_quotes
是必需的,否则 "CMD-SHELL"
中多余的引号会被删除。
上面假设Python3(对于pathlib),如果你还是运行Python2你可以交一个正常打开的普通文件句柄:
with open('test.yaml') as fp:
data = yaml.load(fp)
with open('out.yaml', 'w') as fp:
yaml.dump(data, fp)
我想你可能对 YAML 语法有一些误解。这个:
test: ["this", "is", "a", "list"]
完全等同于:
test:
- this
- is
- a
- list
还有这个:
- "This is a string value"
完全等同于:
- "This is a
string value"
如果我将您的示例放入文件 data.yml
:
$ cat data.yml
healthcheck:
test: ["CMD-SHELL", "[ x\"`curl -k --silent -w '%{http_code}' https://localhost:4433 | grep 401`\" = x\"\" ] && exit 1 || exit 0"]
interval: 30s
然后用PyYAML解析:
>>> import yaml
>>> with open('data.yml') as fd:
... data = yaml.load(fd)
...
我得到以下 Python 数据结构:
>>> pprint.pprint(data)
{'healthcheck': {'interval': '30s',
'test': ['CMD-SHELL',
'[ x"`curl -k --silent -w \'%{http_code}\' https://localhost:4433 | grep 401`" = x"" ] && exit 1 || exit 0']}}
如果我使用 PyYAML 转储它,我会得到:
>>> print yaml.dump(data)
healthcheck:
interval: 30s
test: [CMD-SHELL, '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433
| grep 401`" = x"" ] && exit 1 || exit 0']
...这似乎很好。我可以请求更详细的列表语法,在这种情况下,我会得到你在示例中显示的内容:
>>> print yaml.dump(data, default_flow_style=False)
healthcheck:
interval: 30s
test:
- CMD-SHELL
- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
= x"" ] && exit 1 || exit 0'
...这将解析为与原始文档完全相同的 Python 数据结构。除"looking different"外,实际数据相同。
我已经搜索了几天,试图找出为什么我的 yaml 解析器(使用 PyYaml)没有像原始状态那样保存回 YAML。
YAML 中的原始行是:
healthcheck:
test: ["CMD-SHELL", "[ x\"`curl -k --silent -w '%{http_code}' https://localhost:4433 | grep 401`\" = x\"\" ] && exit 1 || exit 0"]
interval: 30s
但是新行(只是加载文件并再次保存):
healthcheck:
interval: 30s
test:
- CMD-SHELL
- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
= x"" ] && exit 1 || exit 0'
这里有两个问题: 1) "test" 值变成一个列表而不是 1 行键值对。 2)这里实际上有3个换行,
a) -CMD-SHELL
b)- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
c)= x"" ] && exit 1 || exit 0'
那么另一个问题是,为什么第三行是从第二行断的? (如果我显示白色 space,你会看到在第二行的末尾有 LF 然后开始第三行
PyYAML 不太擅长在往返过程中保留样式(加载、修改、安全),它实际上无法通过使用其 load/dump 参数按原样保留您的输入。为此,您需要修改 PyYAML 解析器。
这就是 ruamel.yaml
所做的(免责声明:我是该包的作者),它是专门为支持此类程序化往返(包括保留评论)而开发的:
import sys
import ruamel.yaml
from pathlib import Path
yaml_file = Path('test.yaml')
out_file = Path('out.yaml')
yaml = ruamel.yaml.YAML()
yaml.width = 2048
yaml.preserve_quotes = True
data = yaml.load(yaml_file)
yaml.dump(data, out_file)
这为您提供了一个 out.yaml
,其内容与 test.yaml
.
默认宽度 (80) 会像在 PyYAML 中那样换行,因此将其设置为比最大长度行更长的值。 preserve_quotes
是必需的,否则 "CMD-SHELL"
中多余的引号会被删除。
上面假设Python3(对于pathlib),如果你还是运行Python2你可以交一个正常打开的普通文件句柄:
with open('test.yaml') as fp:
data = yaml.load(fp)
with open('out.yaml', 'w') as fp:
yaml.dump(data, fp)
我想你可能对 YAML 语法有一些误解。这个:
test: ["this", "is", "a", "list"]
完全等同于:
test:
- this
- is
- a
- list
还有这个:
- "This is a string value"
完全等同于:
- "This is a
string value"
如果我将您的示例放入文件 data.yml
:
$ cat data.yml
healthcheck:
test: ["CMD-SHELL", "[ x\"`curl -k --silent -w '%{http_code}' https://localhost:4433 | grep 401`\" = x\"\" ] && exit 1 || exit 0"]
interval: 30s
然后用PyYAML解析:
>>> import yaml
>>> with open('data.yml') as fd:
... data = yaml.load(fd)
...
我得到以下 Python 数据结构:
>>> pprint.pprint(data)
{'healthcheck': {'interval': '30s',
'test': ['CMD-SHELL',
'[ x"`curl -k --silent -w \'%{http_code}\' https://localhost:4433 | grep 401`" = x"" ] && exit 1 || exit 0']}}
如果我使用 PyYAML 转储它,我会得到:
>>> print yaml.dump(data)
healthcheck:
interval: 30s
test: [CMD-SHELL, '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433
| grep 401`" = x"" ] && exit 1 || exit 0']
...这似乎很好。我可以请求更详细的列表语法,在这种情况下,我会得到你在示例中显示的内容:
>>> print yaml.dump(data, default_flow_style=False)
healthcheck:
interval: 30s
test:
- CMD-SHELL
- '[ x"`curl -k --silent -w ''%{http_code}'' https://localhost:4433 | grep 401`"
= x"" ] && exit 1 || exit 0'
...这将解析为与原始文档完全相同的 Python 数据结构。除"looking different"外,实际数据相同。