ruamel.yaml: 如何在 YAML 中保留 dict 的结构

ruamel.yaml: How to preserve structure of dict in YAML

我正在使用 ruamel.yaml 编辑 YAML 文件并转储它们。我需要有关如何保持结构与原始文件相同的帮助,

我有一个包含以下内容的 YAML 文件,但是,这个内容没有被修改,但是当我在编辑这个内容的结构后加载和转储它时,它发生了变化

    parameters: {
      "provision_pipeline": "provision-integrations",
      "enable_sfcd_ds_argo_operator": "false",
      "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
    }

但是,在我转储之后,这个结构被更改为以下格式:

    parameters: {"provision_pipeline": "provision-integrations", "enable_sfcd_ds_argo_operator": "false",
      "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"}

代码:

def addTargetToBaseIntegFileAndUpdate(deploymentTarget, fi, env, samvmf_repo, folder, pipelineversionintegration, basefile):
    yamldata = OrderedDict()
    ryaml = rumel.yaml.YAML()
    ryaml.preserve_quotes = True
    ryaml.default_flow_style = False
    ryaml.indent(mapping=2)
        
    with open(basefile, "r") as file:
        yamldata = ryaml.load(file)
        deploymentTargets = yamldata["targets"]["stagger_groups"]
        target = ""
        doesFIExist = False
        fi_index = 0

        for index, sg in enumerate(deploymentTargets):
            if sg["name"] == env.lower():
                target = deploymentTargets[index]
                for i, fi_item in enumerate(target["falcon_instances"]):
                    if fi_item["name"] == fi.lower():
                        fi_index = i
                        doesFIExist = True
                        break
                if doesFIExist:
                    yamldata["targets"]["stagger_groups"][index]["f_instances"][fi_index]["f_domains"].append(deploymentTarget["f_instances"][0]["f_domains"][0])
                else:
                    yamldata["targets"]["stagger_groups"][index]["f_instances"].append(deploymentTarget["f_instances"][0])
                break

    with open(basefile, "w") as fileobj:
        ryaml.dump(yamldata, fileobj)

正如 Nick Bailey 在评论中指出的那样,这是一种风格上的变化,而不是结构上的变化。也就是说,数据是一样的,只是呈现方式不同而已。

现在,至于那种风格是什么,YAML 有 two styles 的呈现数据结构:

  • 块样式:每个key/value都在一个新行开始,列表和字典(映射)都通过缩进启动和停止。这通常是首选样式,因为它更 human-readable.

  • 流式:Lists/mappings以括号开始和结束,多个key/value以逗号分隔,如JSON。 key/value 对之间不需要换行符,但也不是不允许的。这种格式更常用于更小、更简单的数据结构,尤其是在单行上,因为它可以节省 space.

您显示的原始 YAML 是更大 block-style 映射中的一对 key/value,但值本身 不是 块样式;它只是添加了额外换行符的流式样式。我想您可能想要这个,完全采用块样式:

test:
  parameters:
    "provision_pipeline": "provision-integrations"
    "enable_sfcd_ds_argo_operator": "false"
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"

ruamel.yaml,在其默认(往返)模式下,保留流式或块式,无论你给它什么,但我不知道有什么方法可以让它记住你的特定换行符添加在流程部分中。查看此比较:

import sys
from ruamel.yaml import YAML

yaml_string_1 = """\
test:
  parameters: {
    "provision_pipeline": "provision-integrations",
    "enable_sfcd_ds_argo_operator": "false",
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
  }
"""
yaml_string_2 = """\
test:
  parameters:
    "provision_pipeline": "provision-integrations"
    "enable_sfcd_ds_argo_operator": "false"
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
"""

yaml = YAML()
for yaml_string in [yaml_string_1, yaml_string_2]:
    output = yaml.load(yaml_string)
    yaml.dump(output, sys.stdout)
    print()

输出:

test:
  parameters: {provision_pipeline: provision-integrations, enable_sfcd_ds_argo_operator: 'false',
    clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml}

test:
  parameters:
    provision_pipeline: provision-integrations
    enable_sfcd_ds_argo_operator: 'false'
    clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml

当然,您也可以添加 preserve_quotes 以及您需要的任何其他选项。

ruamel.yaml 不保留流样式映射元素之间的换行符。唯一的事情 影响这些的是 yaml.width 所以你可以在越来越长的行上换行。 例如。根据您的输入,如果您将宽度设置为 40,您将得到:

parameters: {"provision_pipeline": "provision-integrations",
  "enable_sfcd_ds_argo_operator": "false",
  "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"}

但是没有控制让你在新的一行上得到第一个 key-value 对,也没有你得到一个结束 大括号单独一行。

您的添加 ryaml.default_flow_style = False 只会影响您添加到的全新字典和列表 数据结构。

你应该考虑切换到块样式并删除所有 non-essential 引号,这使得 YAML 既不冗长又更易读。对于加载数据的程序来说,这没有区别, 并且通过在正常安全模式下加载很容易完成转换(不设置 block/flow-style 信息 在加载的数据上):

import sys
import pathlib
import ruamel.yaml

basefile = pathlib.Path('input.yaml')

data = ruamel.yaml.YAML(typ='safe').load(basefile)
yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)

给出:

parameters:
  provision_pipeline: provision-integrations
  enable_sfcd_ds_argo_operator: 'false'
  clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml

字符串标量 'false' 需要被引用,以免与布尔值 false.

混淆

如果以上改善无法接受,例如如果对某物进行进一步处理 除了完整的 YAML 解析器,您还可以 post-process 输出:

import sys
import pathlib
import ruamel.yaml

basefile = pathlib.Path('input.yaml')

def splitflowmap(s):
    res = []
    for line in s.splitlines():
        if ': {' in line and line[-1] == '}':
            start, rest = line.split(': {', 1)
            start = start + ': {'
            indent = '  '  # two spaces more than the start
            for idx, ch in enumerate(start):
                if ch != ' ':
                    break
                indent += ' '
            res.append(start)
            rest = rest[:-1]  # cut of }\n
            for x in rest.split(', '):  # if you always have quotes it is safer to split on '", "'
                res.append(f'{indent}{x},')
            res[-1] = res[-1][:-1]  # delete trailing comma
            res.append(f'{indent[2:]}}}')  # re-add the cut of }\n on a line of its own
            continue
        res.append(line)
    return '\n'.join(res) + '\n'

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.width = 2**16
data = yaml.load(basefile)
yaml.dump(data, sys.stdout, transform=splitflowmap)

给出:

parameters: {
  "provision_pipeline": "provision-integrations",
  "enable_sfcd_ds_argo_operator": "false",
  "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
}