【问题标题】:Is there a way to preserve order while round-trip dumping YAML in Python?有没有办法在 Python 中往返转储 YAML 时保留订单?
【发布时间】:2020-05-24 17:31:52
【问题描述】:

我正在尝试从 YAML 文件中加载一些数据并将其放回:

services:
  dc01:
    sw-06-50001:
      servers:
      - {ip: 10.255.206.12, port: 50001, weight: 100}
      - {ip: 10.255.206.13, port: 50001, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50001}
    sw-09-50002:
      servers:
      - {ip: 10.255.206.18, port: 50002, weight: 100}
      - {ip: 10.255.206.19, port: 50002, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50002}
    sw-06-50003:
      servers:
      - {ip: 10.255.206.12, port: 50003, weight: 100}
      - {ip: 10.255.206.13, port: 50003, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50003}
    sw-09-50004:
      servers:
      - {ip: 10.255.206.18, port: 50004, weight: 100}
      - {ip: 10.255.206.19, port: 50004, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50004}

用这个:

import ruamel.yaml as yaml
with open('filename.yml', 'r') as stream:
    outdata = yaml.load(stream,Loader=yaml.Loader)

yaml.dump(outdata,'filename_out.yml')

我需要保留列表中的所有格式和数据顺序,但输出转储会导致按 sw-xx.. 键按字母顺序排序:

services:
  dc01:
    sw-06-50001:
      servers:
      - {ip: 10.255.206.12, port: 50001, weight: 100}
      - {ip: 10.255.206.13, port: 50001, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50001}

    sw-06-50003:
      servers:
      - {ip: 10.255.206.12, port: 50003, weight: 100}
      - {ip: 10.255.206.13, port: 50003, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50003}
    sw-09-50002:
      servers:
      - {ip: 10.255.206.18, port: 50002, weight: 100}
      - {ip: 10.255.206.19, port: 50002, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50002}
    sw-09-50004:
      servers:
      - {ip: 10.255.206.18, port: 50004, weight: 100}
      - {ip: 10.255.206.19, port: 50004, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50004}

转储时如何保持条目的原始顺序?

【问题讨论】:

  • 你不能,因为那不是一个列表。
  • 在模块 ruamel.yaml 中使用了自定义的 ordereddict(),而不是 dict(),因此在这里使用字典不是问题。正如@Anthon 所说,问题在于使用过时的库兼容性而不是正常使用。

标签: python-3.x ruamel.yaml


【解决方案1】:

您正在执行往返,但您没有使用 ruamel.yaml 的往返功能。相反,你是 使用过时的 PyYAML 兼容性 loaddump 函数。

相反,您应该实例化一个ruamel.yaml.YAML() 实例并使用该实例的loaddump 方法, 它们默认为往返并保留顺序(以及 cmets、标签名称等)

import sys
import ruamel.yaml

yaml_str = """\
services:
  dc01:
    sw-06-50001:  # first
      servers:
      - {ip: 10.255.206.12, port: 50001, weight: 100}
      - {ip: 10.255.206.13, port: 50001, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50001}
    sw-09-50002:
      servers:
      - {ip: 10.255.206.18, port: 50002, weight: 100}
      - {ip: 10.255.206.19, port: 50002, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50002}
    sw-06-50003:  # third
      servers:
      - {ip: 10.255.206.12, port: 50003, weight: 100}
      - {ip: 10.255.206.13, port: 50003, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50003}
    sw-09-50004:
      servers:
      - {ip: 10.255.206.18, port: 50004, weight: 100}
      - {ip: 10.255.206.19, port: 50004, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50004}
"""

yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

给出:

services:
  dc01:
    sw-06-50001:  # first
      servers:
      - {ip: 10.255.206.12, port: 50001, weight: 100}
      - {ip: 10.255.206.13, port: 50001, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50001}
    sw-09-50002:
      servers:
      - {ip: 10.255.206.18, port: 50002, weight: 100}
      - {ip: 10.255.206.19, port: 50002, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50002}
    sw-06-50003:  # third
      servers:
      - {ip: 10.255.206.12, port: 50003, weight: 100}
      - {ip: 10.255.206.13, port: 50003, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50003}
    sw-09-50004:
      servers:
      - {ip: 10.255.206.18, port: 50004, weight: 100}
      - {ip: 10.255.206.19, port: 50004, weight: 90}
      virtual: {ip: 192.168.1.4, port: 50004}

或者,如果您在文件中有 YAML:

from pathlib import Path
from ruamel.yaml import YAML

# the recommended extension for YAML files has been .yaml since 2006.
file_name = Path('filename.yaml')  
file_out = Path('filename_out.yaml')

yaml = YAML()
outdata = yaml.load(file_name)
yaml.dump(outdata, file_out)

【讨论】:

  • 谢谢!像魅力一样工作!根据您的模块文档,当我开始调查该行为时,我注意到它应该使用 dict() 而不是 ordereddict() ,但不知道兼容性的东西。顺便说一句,在这种情况下,我可以在这个有序 dict 中的某个位置放置一些键(比如说,在 ordereddict() 中间添加另一个“sw-XX-”以保持自定义排序到位?
  • outdata['services']['dc01']ruamel.yaml.comments.CommentedMap 的一个实例,该类有一个insert(self, pos, key, value, comment=None) 方法。
  • 知道了!感谢您的帮助!
猜你喜欢
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多