PyYAML 转储格式

Posted

技术标签:

【中文标题】PyYAML 转储格式【英文标题】:PyYAML dump format 【发布时间】:2014-01-15 07:47:20 【问题描述】:

我知道在 SO 上有一些关于此的问题,但我找不到我要查找的内容。

我正在使用pyyaml 读取(.load().yml 文件,修改或添加密钥,然后再次写入(.dump())。问题是我想在转储后保留文件格式,但它发生了变化。

例如,我将键 en.test.index.few 编辑为 "Bye" 而不是 "Hello"

Python:

with open(path, 'r', encoding = "utf-8") as yaml_file:
    self.dict = pyyaml.load(yaml_file)

然后,在更改密钥之后:

with open(path, 'w', encoding = "utf-8") as yaml_file:
    dump = pyyaml.dump(self.dict, default_flow_style = False, allow_unicode = True, encoding = None)
    yaml_file.write( dump )

Yaml:

之前:

en:
  test:
    new: "Bye"
    index:
      few: "Hello"
  anothertest: "Something"

之后:

en:
  anothertest: Something
  test:
    index:
      few: Hello
    new: Bye

有没有办法保持相同的格式?例如 qoutes 和 order。我是否为此使用了错误的工具?

我知道原始文件可能并不完全正确,但我无法控制它(它是一个 Ruby on Rails i18n 文件)。

非常感谢。

【问题讨论】:

yaml.dump 有一个 default_style 参数。使用default_style='"' 会将您的字符串值保留在双引号中,但您的键和任何其他值类型也将用双引号括起来。 谢谢!,我会记住的,如果不是钥匙,它会非常有用:( 您可能也很难订购钥匙。 yaml.load 给你一个dict;它的键是无序的。 yaml.dump 可能以迭代进行的任何顺序输出。 新文件表示与原始文件完全相同的信息(在 YAML 中);没有理由保持相同的格式。 @Evert 没错,但我想保留格式,因为考虑到我创建的 sublime 包的上下文,它很有用github.com/NicoSantangelo/sublime-text-i18n-rails 【参考方案1】:

下面,使用ruamel.yaml 代替。

ruamel.yaml 是 actively maintained。与 PyYAML 不同,ruamel.yaml 支持:

YAML PyYAML 仅支持 YAML breaks backward compatibility。这通常是一件坏事。在这种情况下,这使 YAML 1.2 成为 JSON 的严格超集。由于 YAML 1.1 不是 JSON 的严格超集,这是一件好事。 往返保存。 当调用 yaml.dump() 转储先前调用 yaml.load() 加载的字典时: PyYAML 天真地忽略所有输入格式 - 包括 cmets、排序、引用和空格。像这么多数字垃圾一样被丢弃到最近的可用比特桶中。 ruamel.yaml 巧妙地尊重所有输入格式。一切。整个风格的辣酱玉米饼馅。整个文学界。 全部。

库迁移

在现有应用程序中从 PyYAML 切换到 ruamel.yaml 通常只需将库导入更改为:

from ruamel import yaml

这是因为ruamel.yaml 是一个符合 PyYAML API 的 PyYAML 分支。

不需要进行其他更改。 yaml.load()yaml.dump() 函数应继续按预期运行。

往返保存以及它可以为您做什么

为了向后兼容 PyYaml,yaml.load()yaml.dump() 函数默认执行往返保存。为此,请明确传递:

yaml.load() 的可选 Loader=ruamel.yaml.RoundTripLoader 关键字参数。 yaml.dump() 的可选 Dumper=ruamel.yaml.RoundTripDumper 关键字参数。

一个从ruamel.yaml documentation“借来”的例子:

import ruamel.yaml

inp = """\
# example
name:
  # Yet another Great Duke of Hell. He's not so bad, really.
  family: TheMighty
  given: Ashtaroth
"""

code = ruamel.yaml.load(inp, Loader=ruamel.yaml.RoundTripLoader)
code['name']['given'] = 'Astarte'  # Oh no you didn't.

print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')

大功告成。现在将原封不动地保留注释、排序、引用和空格。

【讨论】:

我必须说这是一个很棒的答案。我目前没有开发使用 PyYAML 的项目,但是当我有空闲时间并接受答案时,我肯定会尝试ruamel.yaml。谢谢! @sjmh 从 ruamel.yaml 0.11.12 开始,您可以在加载期间指定 preserve_quotes=True,这将包装加载的字符串,其中包含转储所需的信息。另见this answer PyYAML 现在有了新的维护者,并且最近发布了 v4.1。答案已经过时,诸如“PyYAML 是一具腐烂的恶臭尸体”之类的愚蠢内容可能应该被删除。 3.x 到 4.x 是一个主要的版本号颠簸,因此应该预料到向后不兼容的变化。我并不否认 PyYAML 维护存在问题和政治问题,但这个答案中使用的语言有点过分。它读起来像是 ruamel.yaml 或宣传的广告。 这个答案应该更正。它立即以虚假陈述开始。也许他们在写它的时候是正确的,但情况不再如此。 PyYaml 并没有死掉,网站已经上线了。在撰写此评论的那一刻,PyYaml 显得非常活跃和活跃。查看最新版本: - 2019-07-30:PyYAML 5.1.2 发布。 - 2018-06-06:PyYAML 5.1.1 发布。 - 2019-03-13:LibYAML 0.2.2 和 PyYAML 5.1 发布。 - 2018-07-05:PyYAML 3.13 发布。 - 2018-06-24:LibYAML 0.2.1 发布。这个答案具有误导性。【参考方案2】:

就我而言,如果值包含,我想要",否则什么都没有。例如:

 en:
   key1: value is 1
   key2: 'value is 1'

要执行此操作,请从模块 PyYaml 中的文件 representer.py 中复制函数 represent_str(),如果字符串包含 ,则使用另一种样式:

def represent_str(self, data):
    tag = None
    style = None
    # Add these two lines:
    if '' in data or '' in data:
        style = '"'
    try:
        data = unicode(data, 'ascii')
        tag = u'tag:yaml.org,2002:str'
    except UnicodeDecodeError:
        try:
            data = unicode(data, 'utf-8')
            tag = u'tag:yaml.org,2002:str'
        except UnicodeDecodeError:
            data = data.encode('base64')
            tag = u'tag:yaml.org,2002:binary'
            style = '|'
    return self.represent_scalar(tag, data, style=style)

在您的代码中使用它:

import yaml

def represent_str(self, data):
  ...

yaml.add_representer(str, represent_str)

在这种情况下,键和值之间没有差异,这对我来说就足够了。如果您想要不同的键和值样式,请使用函数 represent_mapping 执行相同的操作

【讨论】:

【参考方案3】:

第一

使用以下代码表示字典数据:

mapping = list(mapping.items())
    try:
        mapping = sorted(mapping)
    except TypeError:
        pass

这就是改变顺序的原因

第二

有关标量类型如何呈现的信息(是否使用双引号)在阅读时会丢失(这是库的主要方法)

总结

您可以基于“Dumper”创建自己的类并重载方法“represent_mapping”以更改字典的呈现方式

要保存有关标量双引号的信息,您还必须基于“加载器”创建自己的类,但我担心它会影响其他类并且会很难做到

【讨论】:

以上是关于PyYAML 转储格式的主要内容,如果未能解决你的问题,请参考以下文章

为 PyYAML 转储的一部分指定样式

强制 pyYAML 持续转储

我可以在 yaml/pyyaml 中转储空白而不是 null 吗?

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

在 PyYAML 中保存/转储带有注释的 YAML 文件

PyYAML 可以按非字母顺序转储 dict 项目吗?