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

ruamel.yaml修改YAML为何在文件末尾额外新增expiration键值对?

问题背景

使用ruamel.yaml解析独立YAML文件,需求是更新文件内的expiration日期,同时保留全部原有内容。相同代码处理两个结构略有差异的文件时,一个运行正常,另一个会额外新增多余的expiration键值对。

测试文件原始内容

  • File_one:
---
value: 600
another_value: 330
main_config::config::config:
  username:
    password: 'ENCRYPTED_PASSWORD'
    name: 'John Doe'
    role: 'admin'
    expiration: '2055-01-01'
    email: 'username@email.com'
  • File_two:
---
main_config::config::config:
  username:
    password: 'ENCRYPTED_PASSWORD'
    name: 'John Doe'
    role: 'admin'
    expiration: '2055-01-01'
    email: 'username@email.com'
another_config::config:
  setting1:
    setting2:
      setting3:
        setting4: false

异常输出

File_two处理后,会在another_config::config层级下额外新增和setting1同级的expiration键:

---
main_config::config::config:
  username:
    password: 'ENCRYPTED_PASSWORD'
    name: 'John Doe'
    role: 'admin'
    expiration: '2055-01-01'
    email: 'username@email.com'
another_config::config:
  setting1:
    setting2:
      setting3:
        setting4: false
    expiration: '2055-01-01'

复现代码

with open(f"./path/to/file.yaml", 'r') as f:
    file = yaml.load(f)

    for k, v in file.items():
        if isinstance(v, dict):
            for x, y in v.items():
                if isinstance(y, dict):
                    y['expiration'] = DATE
    # 写入更新后的内容
    with open(f"./path/to/file.yaml", "w") as edit_file:
        yaml.dump(file, edit_file)
原因分析

核心问题是遍历逻辑存在缺陷:代码仅做了固定两层的字典遍历,且没有判断目标键是否原本存在,就直接强制写入expiration值。

两个文件输出差异完全由自身结构不同导致:

  • 处理File_one时,根节点下只有main_config::config::config的值是字典,该字典下只有username的值是字典,刚好就是需要修改的用户配置层级,完全匹配两层遍历的逻辑,因此不会产生多余写入,看起来运行正常。
  • 处理File_two时,根节点下有两个值为字典的键:第一个main_config::config::config下的username字典被正确修改;第二个another_config::config下的setting1同样是字典类型,被遍历逻辑命中,被强制写入了原本不存在的expiration键,这就是多余键值对的来源。

简单说,代码的实际逻辑是给所有处于第二层嵌套位置的字典都添加expiration键,File_one刚好没有其他符合条件的字典,所以没有触发bug,File_two存在额外的第二层嵌套字典,就出现了异常写入。

修复方案

不要用固定层数的遍历盲目写入,建议通过递归遍历所有节点,仅对原本就存在expiration键的字典做更新,避免新增多余键:

def update_expiration(node, target_date):
    if isinstance(node, dict):
        # 仅当当前层级已存在expiration键时才更新
        if 'expiration' in node:
            node['expiration'] = target_date
        # 递归处理所有子节点
        for val in node.values():
            update_expiration(val, target_date)

with open("./path/to/file.yaml", 'r') as f:
    content = yaml.load(f)
    update_expiration(content, DATE)
    with open("./path/to/file.yaml", "w") as edit_file:
        yaml.dump(content, edit_file)

内容的提问来源于stack exchange,提问作者anjoola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 07:21:55