Python YAML 文本的字典不保留字符

Python dict to YAML text doesn't preserve characters

问题很简单,但措辞正确有点棘手。

基本上,我有一个包含以下数据的字典:

x = { foo: [1, '\n', 'bar'] }

当我使用带有 yaml.safe_dump(x, default_flow_style=False) 的 pyyaml 转换为 yaml 时,我希望输出为:

foo:
  - 1
  - '\n'
  - bar

然而,我得到了类似

的东西
foo:
  - 1
  - '

    '
  - bar

换行符实际上被解释而不是作为'\n'字符串传递。

我一直在查看 pyyaml 文档,但没有看到正确的咒语来正确解析这个东西。

有没有人以前处理过同样的问题?你是怎么解决的?


为了提供更多背景信息,关于这个。

我有一个json我想转换成yaml。

具有类似内容的文件:

{ 
  "content": {
    "Fn::Join": ["\n", [{ "Ref": "parentStackName" }, ""]]
  }
}

最终结果应该是这样的:

content:
  Fn::Join:
    - "\n"
    - - Ref: parentStackId
      - ''

注意 "\n" 只是一个字符串,而不是实际字符。

我使用的程序是:

  1. 打开文件
  2. 从文本到字典解析json
  3. 使用dict转储到yaml

当我创建字典时,您可以看到“\n”是字符串的一部分。当 pyyaml 将其转储到 yaml 中时,事情就会出错。

要获得所需的输出,您可以使用 ruamel.yaml 的 round-trip 功能,并更新 YAML 的 JSON 子集用来阻止的 flow-style风格:

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq

# because this is a string and not read from file, you need to escape 
# the backslash in \n
json_str = """\
{
  "content": {
    "Fn::Join": ["\n", [{ "Ref": "parentStackName" }, ""]]
  }
}
"""  


def block_style(base):
    """set all mapping and sequneces to block-style"""
    if isinstance(base, CommentedMap):
        for k in base:
            block_style(base[k])
        base.fa.set_block_style()
    if isinstance(base, list):
        for item in base:
            block_style(item)
        base.fa.set_block_style()
    return base


data = ruamel.yaml.round_trip_load(json_str)
block_style(data)
ruamel.yaml.round_trip_dump(data, sys.stdout)

给出:

content:
  Fn::Join:
  - "\n"
  - - Ref: parentStackName
    - ''

ruamel.yaml 是 PyYAML 的更新版本(免责声明:我是作者)。它支持 YAML 1.2 规范(从 2009 年开始),它使 YAML 更符合 JSON 的完整超集,并允许您使用 ruamel.yaml 解析器读取 JSON(PyYAML 仅支持大多数 YAML 1.1 规范)。

在 "round-trip-mode" 中,ruamel.yaml 的增强功能包括保持流程和响应。 block-style 个复合节点(映射和序列)(以及各种引用样式、注释和标签名称)。 block_style() 所做的是递归地将 "flow-attribute" .fa 更改为所有复合节点的块样式。

当字符串包含特殊字符时使用原始字符串

$ cat /tmp/tmp.py

import yaml

foo = 'foo'
x = { foo: [1, r'\n', 'bar'] }
y = yaml.safe_dump(x, default_flow_style=False)
print(y)

$ python3 /tmp/tmp.py
foo:
- 1
- \n
- bar