pyyaml 并仅对字符串使用引号

Posted

技术标签:

【中文标题】pyyaml 并仅对字符串使用引号【英文标题】:pyyaml and using quotes for strings only 【发布时间】:2016-11-17 02:42:36 【问题描述】:

我有以下 YAML 文件:

---
my_vars:
  my_env: "dev"
  my_count: 3

当我用 PyYAML 读取它并再次转储它时,我得到以下输出:

---
my_vars:
  my_env: dev
  my_count: 3

有问题的代码:

with open(env_file) as f:
    env_dict = yaml.load(f)
    print(yaml.dump(env_dict, indent=4, default_flow_style=False, explicit_start=True))

我尝试使用default_style 参数:

with open(env_file) as f:
    env_dict = yaml.load(f)
    print(yaml.dump(env_dict, indent=4, default_flow_style=False, explicit_start=True, default_style='"'))

但现在我明白了:

---
"my_vars":
  "my_env": "dev"
  "my_count": !!int "3"

我需要做什么来保持原始格式,对 YAML 文件中的变量名做出任何假设?

【问题讨论】:

为什么不直接打印文件,重要的是使用字典作为打印内容的来源而不是文件本身吗? 以后我会在字典中添加额外的键,但是布局需要保留。 【参考方案1】:

您可以使用以下方法将您的double quoted scalar对象保留在yaml中:

以你的 yaml 为例:

---
my_vars:
  my_env: "dev"
  my_count: 3

将其加载到 env_dict(字典)中:

myyaml = '''
---
my_vars:
  my_env: "dev"
  my_count: 3
'''

env_dict = yaml.load(myyaml, yaml.FullLoader) # loading yaml

print(env_dict)
'my_vars': 'my_env': 'dev', 'my_count': 3

# Define a quoted class, which uses style = '"' and add representer to yaml

class quoted(str):
    pass

def quoted_presenter(dumper, data):
    return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')
yaml.add_representer(quoted, quoted_presenter)


# Now, we update the dictionary env_dict as follows for the "dev" 
# value which needs to be a double quoted scalar

env_dict['my_vars'].update(my_env = quoted("dev")) # this makes "dev"
# a double quoted scalar

# Now, we dump the yaml as before

yaml.dump(env_dict, sys.stdout, indent=4, default_flow_style=False, explicit_start=True)

# which outputs

---
my_vars:
    my_count: 3
    my_env: "dev"

这些链接帮助我得出了这个答案:Any yaml libraries in Python that support dumping of long strings as block literals or folded blocks?

How can I control what scalar form PyYAML uses for my data?

另外,这篇文章很适合在To Quote or not to Quote?阅读

希望,这会有所帮助!

【讨论】:

【参考方案2】:

我建议您更新为使用带有向后兼容 ruamel.yaml 包的 YAML 1.2(2009 年发布),而不是使用实现大部分 YAML 1.1 (2005) 的 PyYAML。 (免责声明:我是该软件包的作者)。

然后,您只需在加载往返 YAML 文件时指定 preserve_quotes=True

import sys
import ruamel.yaml

yaml_str = """\
---
my_vars:
  my_env: "dev"    # keep "dev" quoted
  my_count: 3
"""

data = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
ruamel.yaml.round_trip_dump(data, sys.stdout, explicit_start=True)

哪些输出(包括保留的评论):

---
my_vars:
  my_env: "dev"    # keep "dev" quoted
  my_count: 3

在加载字符串后,标量将成为字符串的子类,以便能够容纳引用信息,但对于所有其他用途,它们将像普通字符串一样工作。如果你想替换这样的字符串(devfgw) 您必须将字符串转换为该子类(DoubleQuotedScalarString 来自 ruamel.yaml.scalarstring)。

当往返ruamel.yaml 默认情况下会保留键的顺序(通过插入)。

【讨论】:

【参考方案3】:

对,所以从this answer 大量借用,你可以这样做:

import yaml

# define a custom representer for strings
def quoted_presenter(dumper, data):
    return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')

yaml.add_representer(str, quoted_presenter)


env_file = 'input.txt'
with open(env_file) as f:
    env_dict = yaml.load(f)
    print yaml.dump(env_dict, default_flow_style=False)

但是,这只是在字典中的 all 字符串类型上重载它,因此它也会引用键,而不仅仅是值。

打印出来:

"my_vars":
  "my_count": 3
  "my_env": "dev"

这是你想要的吗?不知道你说的变量名是什么意思,你是指键吗?

【讨论】:

这对我有很大帮助,谢谢 :-) 仍然不确定如何仅将其应用于值,而不是键。 如果你有很多空闲时间,那么我建议你去 yaml 的 documentation 了解如何创建代表,然后彻底阅读 YAML 的标签 here。还有 this answer 讨论了 PyYaml 的一个分支,它确实提到了一些关于保持原始格式的内容,可能也会有所帮助。

以上是关于pyyaml 并仅对字符串使用引号的主要内容,如果未能解决你的问题,请参考以下文章

使用 PyYaml 加载特殊字符

PyYaml - 使用特殊字符(即重音符号)转储 unicode

禁用 PyYAML 值转换

“!”上的 pyYAML 错误在一个字符串中

仅对 navigationItem backBarButton Item 使用自定义图像

Python的PyYAML模块详解