python yaml包在不需要时解析新行

时间:2017-10-13 10:24:52

标签: python parsing yaml pyyaml

我现在正在搜索几天,试图找出为什么我的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)"测试"值变为列表而不是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'

所以另一个问题是,为什么第三行从第二行被打破了? (如果我显示空格,你会看到在第二行的末尾它有LF 然后开始第三行

2 个答案:

答案 0 :(得分:1)

我认为您可能对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数据结构。除了"看起来不同",实际数据是相同的。

答案 1 :(得分:0)

PyYAML并不擅长保存往返(加载,修改,安全)的样式,它实际上不能通过使用其加载/转储参数来保留您的输入。为此,您需要修改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.yamltest.yaml完全相同的内容。

默认宽度(80)会像在PyYAML中那样包裹你的线,因此将它设置为比你的最大长度线长的东西。 preserve_quotes是必要的,否则"CMD-SHELL"中多余的引号会被删除。

上面假设Python 3(对于pathlib),如果你仍然运行Python 2,你可以提交一个正确打开的普通文件句柄:

with open('test.yaml') as fp:
    data = yaml.load(fp)
with open('out.yaml', 'w') as fp:
    yaml.dump(data, fp)