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

Pydantic使用对象作为字典键时JSON序列化失败问题咨询

Pydantic非字符串字典键序列化解决方案

核心问题原因

标准JSON规范要求对象键必须为字符串,Python标准库json.dumps的default参数仅处理无法序列化的值,不会处理字典键,这是底层限制。Pydantic的json_encoders配置仅作用在值序列化阶段,因此不会对字典键生效。

可行解决方案

不需要额外找其他数据结构,JSON层面仅支持字符串键的对象,以下方案都不需要自定义全局JSON编码器,符合业务限制:

方案1:字段级自定义序列化器(推荐)

直接给字典字段加序列化逻辑,在Pydantic序列化阶段自动转换键为字符串,无需修改数据构造逻辑。

Pydantic v2 示例:

from pydantic import BaseModel, field_serializer
from typing import Dict

# 你的自定义Identifier类型
class Identifier:
    def __init__(self, val):
        self.val = val
    def __str__(self):
        # 你自定义的转字符串逻辑
        return f"warehouse_{self.val}"

class WarehouseItem(BaseModel):
    name: str
    address: str

class Report(BaseModel):
    warehouses: Dict[Identifier, WarehouseItem]

    @field_serializer("warehouses")
    def serialize_warehouse_keys(self, value: Dict[Identifier, WarehouseItem]) -> Dict[str, WarehouseItem]:
        # 自动将所有Identifier类型的键转为字符串
        return {str(k): v for k, v in value.items()}

# 测试用例
warehouses = [
    {"id": Identifier(1), "name": "一号仓", "address": "北京市朝阳区"},
    {"id": Identifier(2), "name": "二号仓", "address": "上海市浦东新区"}
]
report = Report(
    warehouses={w["id"]: WarehouseItem(name=w["name"], address=w["address"]) for w in warehouses}
)
print(report.model_dump_json(indent=2))

Pydantic v1 示例:

from pydantic import BaseModel
from typing import Dict

class Identifier:
    def __init__(self, val):
        self.val = val
    def __str__(self):
        return f"warehouse_{self.val}"

class WarehouseItem(BaseModel):
    name: str
    address: str

class Report(BaseModel):
    warehouses: Dict[Identifier, WarehouseItem]

    def dict(self, **kwargs):
        raw = super().dict(**kwargs)
        # 序列化前预处理字典键
        raw["warehouses"] = {str(k): v for k, v in self.warehouses.items()}
        return raw

# 调用json()会自动先走dict()预处理,不会触发键类型错误
report = Report(...)
print(report.json(indent=2))

方案2:构造数据时预处理键(最简)

不需要修改Pydantic模型,构造字典时直接将非字符串键转成目标格式,将模型的字典字段类型定义为Dict[str, X]即可:

# 构造数据时直接转键
warehouse_data = {
    str(warehouse.id): {
        "name": warehouse.name,
        "address": warehouse.address,
    }
    for warehouse in warehouses
}

# 模型字段直接定义为字符串键,无任何序列化问题
class Report(BaseModel):
    warehouses: Dict[str, WarehouseItem]

方案3:自定义可序列化字典子类

如果有多处同类场景,可以封装一个自动转键的字典子类,Pydantic会自动识别序列化:

from collections import UserDict

class AutoStrKeyDict(UserDict):
    def __init__(self, data=None):
        super().__init__({str(k): v for k, v in (data or {}).items()})

# 模型中直接使用该类型
class Report(BaseModel):
    warehouses: AutoStrKeyDict[WarehouseItem]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 19:54:00