JSON 转 Rust Struct — 从 JSON 生成 Rust Struct 类型
用 serde 从 JSON 生成 Rust struct 类型。可空字段用 Option、snake_case 用 rename_all、缺失字段用 #[serde(default)] —— 浏览器内完成,无需上传。
JSON to Rust Struct
Convert JSON to Rust struct with serde derive macros. Works entirely in your browser.
Rust 的类型系统以严格著称,而 serde 是让 JSON 处理切实可用的桥梁。Deserialize derive 宏本身就是一段会替你写出解析逻辑的代码生成器:一个带有 #[derive(Deserialize)] 的 struct,只需一次 serde_json::from_str::<MyStruct>(&json_str)? 调用,就能由 JSON 字符串实例化;编译器在代码运行之前,就会校验每一个字段类型是否可被反序列化。JSON 转 Rust struct 生成器遍历一条样例 JSON 响应,直接输出与之匹配的 struct —— 一件手做要五分钟(把字段从 camelCase 改成 snake_case、在 Option 与非 Option 之间做选择、设置默认值)、机器做只要五毫秒的事情。
不那么显而易见的、Rust 特有的关注点,是命名约定的桥接,以及 default/optional 的区分。Rust 的约定是 snake_case(user_id),而 JSON 的约定则因 API 而异 —— 大多数 API 用 camelCase(userId),部分用 PascalCase(UserId),内部 API 也可能原生就是 snake_case。serde 的 rename_all 属性可以全局处理大小写转换。Option 与 #[serde(default)] 的区分则更微妙:Option<T> 覆盖字段存在但值为 null 的情形,#[serde(default)] 覆盖字段在 JSON 对象中完全缺失的情形。大多数 API 同时需要这两者。
生成 struct 之后,最常见的下一步是把它交给 reqwest 或 ureq 来消费 API:用 .json::<MyStruct>() 直接反序列化响应体,把解析交给 serde。在用 Tokio 的异步 Rust 里,把 struct 包进 Arc<RwLock<MyStruct>> 即可共享可变访问;对接数据库时,在 serde derive 之外再加上 #[derive(sqlx::FromRow)] 或 Diesel 的 #[derive(Queryable)]。生成的 struct 是数据契约 —— 异步运行时、数据库映射、错误处理,这些才属于开发者补足的部分。
使用方法
粘贴你的 JSON
把 JSON 对象或对象数组放入输入框。生成器遍历结构,为根节点输出一个 Rust struct,并为每个对象形态各生成一个嵌套 struct。
选择 serde 选项
切换 `rename_all = camelCase` 以匹配 camelCase 风格的 JSON key、可空字段用 `Option<T>`、在部分 API 响应中可能缺失的字段加 `#[serde(default)]`。
复制或下载 .rs 文件
把 Rust struct 复制到剪贴板,或下载为 .rs 文件。输出顶部包含 `use serde::{Deserialize, Serialize};` —— 粘进一个模块即可编译。
常见问题
为什么要 derive Deserialize 和 Serialize?
serde 的 `#[derive(Deserialize, Serialize)]` 是在 Rust 中启用自动 JSON 序列化的标准方式。`Deserialize` 用于在运行时把 JSON 解析进 struct;`Serialize` 可选,但默认会带上,以便 struct 正确地完成 JSON 往返。
rename_all 是怎么处理 JSON key 大小写的?
在 struct 级别写 `#[serde(rename_all = "camelCase")]`,就是告诉 serde 把 Rust 的 snake_case 字段名(`user_id`)映射到 JSON 的 camelCase key(`userId`)。重命名对所有字段生效,单个字段可以用 `#[serde(rename = "custom_name")]` 覆盖。
null 字段和缺失字段分别怎么处理?
样本中为 null 的字段会变成 `Option<T>`。在部分数组元素中缺失的字段,会在 `Option<T>` 之外再加上 `#[serde(default)]`,这样 JSON 中缺失的 key 在反序列化时变成 `None`,而不是触发解析错误。
enums 和 tagged unions 怎么处理?
生成器不会从一组已知字符串值中推断出 Rust enum。一个取值是 "active"、"pending"、"closed" 的 status 字段,会被输出为 `String`。enum 和对应的 serde tag 需要在生成后手动写。
能处理 chrono 和 uuid 类型吗?
默认情况下,ISO 8601 日期字符串会变成 `String`。开启 "chrono" 开关后,会输出 `chrono::NaiveDateTime`,并为 ISO 8601 格式加上 `serde::with` 注解。UUID 字符串默认也是 `String` —— 如果要用,自己手动加上 `uuid` crate。
限制说明
- 不支持 serde flatten生成器不会对那些应当在父级内联的嵌套 JSON 对象使用 `#[serde(flatten)]`。对于嵌套很深、但应用层按扁平结构处理的 API,自己手动加 flatten。
- 不支持泛型参数输出是一个具体 struct,字段类型也都是具体的。一个多态的 JSON 响应(比如 `{ "type": "image", "data": ... }`),需要手写一个使用 serde internally tagged 形式的 enum。
- 不考虑借用所有字段类型都拥有自己的数据(`String`、`Vec`……),不使用生命周期或借用。若要用 `Cow<'_, str>` 做零拷贝反序列化,自己手动加上生命周期和 `#[serde(borrow)]` 注解。
平台说明
- macOS
- RustRover、装了 rust-analyzer 的 VS Code,以及带 Rust 支持的 vim,都能直接处理生成的 struct。在 API 探索阶段,从样例响应临时生成时,用这个浏览器工具最合适。
- Windows
- 生成的 struct 在任何 Rust 工具链上都能编译。不想为一次性 struct 生成而打开完整 IDE 时,用这个浏览器工具最合适。
- Linux
- 命令行场景下,`quicktype --lang rust` 或 `serde-generate` 能产出相近的输出。不方便安装 Rust 时,这个浏览器工具是临时生成的合适选择。
- Web
- 完全在客户端运行,生成只需几毫秒。