在 Python 中往返转储 YAML 时有没有办法保持顺序?

Is there a way to preserve order while round-trip dumping YAML in Python?

我正在尝试从 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 的往返功能。相反,你是 使用过时的 PyYAML 兼容性 loaddump 函数。

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

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)