跳到主要内容

JSON 转 Python dataclass:类型注解、嵌套、Optional 一步到位

把一段 API 返回的 JSON 粘进来,直接拿到带类型注解的 @dataclass 或 Pydantic 模型,嵌套自动抽类,可选字段自动标 Optional,省去手写一堆 Python 类的重复劳动。

发布于 作者 李雷
#JSON #Python #dataclass #Pydantic #类型注解

JSON 转 Python dataclass:类型注解、嵌套、Optional 一步到位

接第三方 API 的时候,最磨人的不是逻辑,是开头那段抄字段。返回里 user 对象三十多个键,你照着文档一个一个敲属性名、配类型注解、把嵌套的 address 再单独抽一个类。抄完还得校对,漏一个键或者类型写错,跑到一半才报错。把这件事交给 JSON 转 Python dataclass,粘一段 JSON,出来的就是能直接拖进 .py 的类。

为什么不该手写这些类

手写有三个固定的坑。一是嵌套对象要单独定义类,层一深就容易漏;二是 JSON 只有一种数字类型,你得自己判断某个字段到底是 int 还是 float;三是 key 里常有 userIdis-active2fa 这种不合法的 Python 标识符,得改名又得保留原始 key 做序列化。这三件事都是机械活,出错率高,价值低。让工具按规则推断,比人眼对照文档可靠得多。

@dataclass 还是 Pydantic

工具一个开关切两种输出,用同一份推断结果。@dataclass 是标准库自带,零依赖、零运行时校验开销,适合你已经信任的内部数据,比如读自己的 config.json。Pydantic v2 适合数据来自进程外的场景:API 响应、webhook、用户上传。它的 model_validate(data) 会自动做类型强制和校验,坏数据直接报错,还白送 .model_dump_json()。判断标准很简单:数据跨不跨信任边界。跨,就用 Pydantic。

一段真实的输入输出

假设你 curl 一个接口,拿到这样一行:

{
  "user_id": 42,
  "name": "Lei",
  "score": 9.5,
  "address": { "city": "Hangzhou", "zip": null }
}

@dataclass,根名设为 User,出来是:

from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional


@dataclass
class UserAddress:
    city: str
    zip: Optional[Any] = None


@dataclass
class User:
    user_id: int
    name: str
    score: float
    address: UserAddress

注意几处推断:42 推成 int,9.5 带小数推成 float,只出现过 nullzip 成了 Optional[Any],嵌套的 address 自动抽成 UserAddress 类,而且叶子类排在前面,文件从上往下读每个引用都能解析,不需要前向声明。

Optional 是怎么判出来的

字段带 = None 有两个触发条件。一个是值在 JSON 里出现过 null。另一个更实用:把多次响应作为数组粘进来,某个 key 在一部分元素里有、另一部分里没有,工具就把它标成 Optional[T] = None。比如某个 webhook 有时带 cancelled_at 有时不带,记几条粘进来,它会自动判成可选。单个对象没有"键缺失"这个信号,所以所有字段都算必需;想从一个样本探索可选性,把它包成 [{...}] 再粘。

嵌套、数组与字段排序

对象数组会折叠成一个类。[{"a":1},{"a":1,"b":2}] 出来是一个同时带 ab 的模型,b 因为第一个元素没有被标成 Optional[int] = None,而不是两个几乎重复、要你手动对齐的类。标量列表推成 list[int]list[str],对象列表推成 list[SomeModel]。字段排序遵守 Python 规则,有默认值的不会排在无默认值前面,可变默认值用 field(default_factory=list) 绕开共享可变对象那个经典 bug。不合法的 key 清洗成 snake_case 后,原始 key 用 Field(alias=...)field(metadata=...) 保留,序列化往返不丢真实 key。

我自己的用法

我接外部接口的固定流程是先 curl 一次,把响应丢进 JSON 格式化工具看清结构,再整段粘进 dataclass 生成器,根名按接口语义命名,选 Pydantic,复制进 schemas.py。原本 data["user"]["profile"]["id"] 那种字符串索引,变成 data.user.profile.id 的属性访问,mypy 在编辑器里就能抓出拼写错误,而不是等到生产环境炸。一个三十字段的接口,从粘贴到能用不到一分钟。

所有解析都在浏览器里跑,JSON 不碰服务器,敏感 payload 直接复制生成的 Python 即可,不必分享链接。


Made by Toolora · Updated 2026-06-13