使用ruamel.yaml的RoundTripLoader加载YAML时,如何获取完整属性创建Pydantic模型?
问题解决:ruamel.yaml RoundTripLoader 锚点别名属性缺失及Pydantic适配
问题场景
你使用ruamel.yaml的RoundTripLoader(typ='rt')加载包含锚点与合并键的YAML文件:
- database: dev_db <<: &defaults adapter: postgres host: localhost username: postgres password: password - database: test_db <<: *defaults - database: prod_db <<: *defaults
编写的Python代码如下:
from pydantic import BaseModel from ruamel.yaml import YAML yaml = YAML(typ='rt') with open('config.yaml', 'r') as file: envs = yaml.load(file) for env in envs: print(env)
运行后输出仅显示database字段,缺失锚点引用的属性:
{'database': 'dev_db'} {'database': 'test_db'} {'database': 'prod_db'}
但切换为safe loader(typ='safe')时能正确输出所有属性,你需要基于YAML条目创建Pydantic数据模型,同时保留RoundTripLoader的特性。
原因分析
RoundTripLoader的核心设计是保留YAML原始结构、注释、格式,因此默认不会自动展开<<(合并键)引用的锚点内容——这是为了保证后续将数据序列化回写时,能完全还原原YAML的结构。而safe loader则会直接解析合并所有关联属性,不保留原始结构信息。
解决方案
1. 手动展开合并键获取完整属性
RoundTripLoader加载后的每个条目是ruamel.yaml.comments.CommentedMap类型,该类型提供了merge()方法,可手动触发合并键的展开:
from pydantic import BaseModel from ruamel.yaml import YAML yaml = YAML(typ='rt') with open('config.yaml', 'r') as file: envs = yaml.load(file) for env in envs: env.merge() # 展开<<引用的属性 print(dict(env)) # 输出完整属性
运行后即可得到包含所有字段的输出:
{'database': 'dev_db', 'adapter': 'postgres', 'host': 'localhost', 'username': 'postgres', 'password': 'password'} {'database': 'test_db', 'adapter': 'postgres', 'host': 'localhost', 'username': 'postgres', 'password': 'password'} {'database': 'prod_db', 'adapter': 'postgres', 'host': 'localhost', 'username': 'postgres', 'password': 'password'}
2. 转换为Pydantic数据模型
先定义对应的数据模型,再将展开后的CommentedMap转为字典,传入模型构造方法:
from pydantic import BaseModel from ruamel.yaml import YAML # 定义Pydantic数据模型 class DBConfig(BaseModel): database: str adapter: str host: str username: str password: str yaml = YAML(typ='rt') with open('config.yaml', 'r') as file: envs = yaml.load(file) # 处理每个条目并转换为模型 db_configs = [] for env in envs: env.merge() config = DBConfig(**dict(env)) db_configs.append(config) # 验证模型数据 for config in db_configs: print(config)
输出示例:
database='dev_db' adapter='postgres' host='localhost' username='postgres' password='password' database='test_db' adapter='postgres' host='localhost' username='postgres' password='password' database='prod_db' adapter='postgres' host='localhost' username='postgres' password='password'
补充说明
- 如果不需要保留YAML原始格式、注释,直接使用
typ='safe'更简便,无需手动调用merge(); - 调用
merge()后,原CommentedMap会被修改,若需保留原始结构,可先创建副本再执行合并:env_copy = env.copy(); env_copy.merge()。
内容的提问来源于stack exchange,提问作者Sirish Kumar Bethala
相关产品推荐
相关产品推荐

