You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PyYAML处理注释数据时如何保留YAML中的引号

使用PyYAML处理注释数据时如何保留YAML中的引号

问题描述

我正在处理一个YAML文件,里面的注释带有需要保留引号的特定值。输入示例如下:

example.com/:
 1. catalog-item-20='server1.test.local:80'
 2. network-policy-version=v14.yaml
openshift.io/:
 3. sa.scc.mcs='s0,c107,c49'
collectord.io/:
 4. logs-index=channel_1
 5. logs-override.11-match='^.*(%SENSITIVE%).*$' 

我需要处理这个YAML,输出时对包含特殊字符的值保留正确的引号格式,期望输出如下:

annotations:
    example.com/catalog-item-20: 'server1.test.local:80'
    example.com/network-policy-version: v14.yaml
    openshift.io/sa.scc.mcs: 's0,c107,c49'
    collectord.io/logs-index: channel_1
    collectord.io/logs-override.11-match: '^.*(%SENSITIVE%).*$'

需要注意:

  • 输入中带引号的值,输出里要保留引号
  • 输入中不带引号的值,输出里也保持不带引号
  • 引号的保留要和值的内容无关(不管有没有特殊字符、逗号、冒号等)

我现在写了一段Python代码,用PyYAML来处理,但输出没保留住正确的引号:

import yaml

with open('annotations.yaml', 'r') as f:
    raw_annotations = yaml.safe_load(f)
    
annotations = {}
for annotations_prefix, annotations_body in raw_annotations.items():
    prefix = annotations_prefix if annotations_prefix.endswith('/') else f"{annotations_prefix}/"
    for value in annotations_body:
        if '=' in value:
            annotation_key, annotation_value = value.split('=', 1)
            if annotation_value.startswith("'") and annotation_value.endswith("'"):
                annotation_value = annotation_value[1:-1]
            full_key = f"{prefix}{annotation_key}"
            annotations[full_key] = annotation_value

namespace_content = {
    'apiVersion': 'v1',
    'kind': 'Namespace',
    'metadata': {
        'annotations': annotations
    }
}

with open('namespace.yaml', 'w') as f:
    yaml.dump(namespace_content, f, default_flow_style=False)

这段代码生成的输出没有正确保留引号,比如:

annotations:
    example.com/catalog-item-20: server1.test.local:80
    openshift.io/sa.scc.mcs: s0,c107,c49

我已经尝试了几种方法,但都没解决:

  • 设置default_style='"'或者default_style="'",但这样会给所有值都加上引号
  • 用ruamel.yaml并设置preserve_quotes=True,但没起作用
  • 尝试了yamlcore包,同样无法保留引号

有没有办法让PyYAML完全按照输入文件里的样子保留引号?

解决方案

PyYAML本身的设计是将YAML解析为Python原生数据类型,会丢失所有原始格式信息(包括引号),所以没法直接用它实现你的需求。推荐使用ruamel.yaml——它专门为保留YAML原始格式而设计,能完美解决这个问题。

步骤1:安装ruamel.yaml

pip install ruamel.yaml

步骤2:修改代码

我们需要用ruamel的特殊字符串类型标记不同的引号状态,同时开启格式保留:

from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import SingleQuotedScalarString, PlainScalarString

# 初始化YAML对象,开启引号保留
yaml = YAML()
yaml.preserve_quotes = True

with open('annotations.yaml', 'r') as f:
    raw_annotations = yaml.load(f)
    
annotations = {}
for annotations_prefix, annotations_body in raw_annotations.items():
    # 确保前缀结尾带有斜杠
    prefix = annotations_prefix if annotations_prefix.endswith('/') else f"{annotations_prefix}/"
    for value in annotations_body:
        if '=' in value:
            # 移除输入中的序号前缀(比如"1. ")
            cleaned_entry = value.split('. ', 1)[1]
            annotation_key, annotation_value = cleaned_entry.split('=', 1)
            full_key = f"{prefix}{annotation_key}"
            
            # 根据输入的引号状态,转换为对应的ScalarString类型
            if annotation_value.startswith("'") and annotation_value.endswith("'"):
                # 去掉引号后,标记为单引号字符串
                annotations[full_key] = SingleQuotedScalarString(annotation_value[1:-1])
            else:
                # 标记为无引号的纯字符串
                annotations[full_key] = PlainScalarString(annotation_value)

# 构建Namespace配置
namespace_content = {
    'apiVersion': 'v1',
    'kind': 'Namespace',
    'metadata': {
        'annotations': annotations
    }
}

# 写入输出文件
with open('namespace.yaml', 'w') as f:
    yaml.dump(namespace_content, f)

为什么这个方法有效?

  1. ruamel.yaml的格式保留特性:它会跟踪YAML元素的原始格式信息,而不是像PyYAML那样只解析成原生类型
  2. ScalarString类型标记:SingleQuotedScalarString和PlainScalarString明确告诉ruamel要输出的字符串格式,确保引号状态和输入一致
  3. preserve_quotes=True:阻止ruamel自动修改引号格式,严格按照标记输出

你之前用ruamel失败的原因

你之前可能只是开启了preserve_quotes=True,但没有将split出来的字符串转换为对应的ScalarString类型。因为split得到的是普通Python字符串,ruamel无法识别它原本的引号状态,所以需要手动标记类型才能保留格式。

这样处理后,输出的YAML就会完全符合你的期望,严格保留输入中的引号状态!

备注:内容来源于stack exchange,提问作者user2339149

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 08:43:11