目录
- Rust serde_json 使用详解
- 二、核心概念
- 三、最基本的结构体与 JSON 转换
- 四、JSON 字符串转换为 Rust 结构体
- 五、为什么推荐使用原始字符串
- 六、常用函数总结
- 七、使用 serde_json::Value 处理动态 JSON
- 八、使用 json! 宏创建 JSON
- 九、修改动态 JSON
- 十、Serde 字段属性详解
- 十一、Option<T> 处理可选字段
- 十二、嵌套结构体
- 十三、JSON 数组
- 十四、处理 HashMap
- 十五、使用 flatten 展开字段
- 十六、拒绝未知字段
- 十七、枚举与 JSON
- 十八、Rust 数据与 Value 相互转换
- 十九、读取 JSON 文件
- 二十、将 JSON 写入文件
- 二十一、处理字节数组
- 二十二、通用 API 响应结构
- 二十三、固定字段加动态字段
- 二十四、错误处理
- 二十五、语法错误和数据错误的区别
- 二十六、处理字段可能是字符串或数字
- 二十七、连续读取多个 JSON 对象
- 二十八、一个较完整的配置文件案例
- 二十九、结构体还是 Value,应该怎么选
- 三十、常见问题与踩坑点
- 三十一、推荐的项目使用方式
- 三十二、最终记忆表
Rust serde_json 使用详解
serde_json 是 Rust 生态中最常用的 JSON 处理库,主要负责:
- 将 Rust 数据转换成 JSON:序列化
- 将 JSON 转换成 Rust 数据:反序列化
- 使用
Value动态读取、修改 JSON - 从字符串、字节数组、文件、网络流中读取 JSON
- 将 JSON 写入字符串、字节数组、文件或网络流
严格来说:
serde定义通用的序列化、反序列化框架;serde_json是 Serde 针对 JSON 格式的具体实现。 (Docs.rs)
一、添加依赖
在 Cargo.toml 中添加:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
也可以执行:
cargo add serde --features derive cargo add serde_json
其中:
features = ["derive"]
用于开启:
#[derive(Serialize, Deserialize)]
自动派生宏。Serde 官方文档明确说明,Serialize 和 Deserialize 的派生宏需要启用 derive 功能。 (Docs.rs)
二、核心概念
1. 序列化 Serialize
把 Rust 数据转换为 JSON:
Rust 结构体
↓
JSON 字符串
例如:
User {
id: 1001,
name: "张三",
}
转换成:
{
"id": 1001,
"name": "张三"
}
对应 Trait:
serde::Serialize
2. 反序列化 Deserialize
把 JSON 转换为 Rust 数据:
JSON 字符串
↓
Rust 结构体
对应 Trait:
serde::Deserialize
Serde 已经为大量 Rust 基础类型、集合类型提供了实现,也可以通过派生宏自动为结构体和枚举生成实现。 (Docs.rs)
三、最基本的结构体与 JSON 转换
1. Rust 结构体转换成 JSON
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
active: bool,
roles: Vec<String>,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
active: true,
roles: vec![
"admin".to_string(),
"operator".to_string(),
],
};
let json_text = serde_json::to_string(&user)?;
println!("{json_text}");
Ok(())
}
输出:
{"id":1001,"username":"zhangsan","active":true,"roles":["admin","operator"]}
这里的:
serde_json::to_string(&user)
返回类型为:
Result<String, serde_json::Error>
其作用是将实现了 Serialize 的数据序列化为紧凑 JSON 字符串。 (Docs.rs)
2. 输出格式化 JSON
使用:
serde_json::to_string_pretty()
完整示例:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
active: bool,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
active: true,
};
let json_text = serde_json::to_string_pretty(&user)?;
println!("{json_text}");
Ok(())
}
输出:
{
"id": 1001,
"username": "zhangsan",
"active": true
}
to_string_pretty 适合:
- 配置文件
- 日志输出
- 调试
- 需要人工阅读的 JSON
而 to_string 更适合:
- HTTP API 返回
- 网络传输
- 数据库存储
- 减少数据体积
to_string_pretty 返回格式化后的 JSON 字符串。 (Docs.rs)
四、JSON 字符串转换为 Rust 结构体
使用:
serde_json::from_str()
示例:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
active: bool,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"active": true
}
"#;
let user: User = serde_json::from_str(json_text)?;
println!("用户ID:{}", user.id);
println!("用户名:{}", user.username);
println!("是否启用:{}", user.active);
Ok(())
}
输出:
用户ID:1001 用户名:zhangsan 是否启用:true
注意这里必须明确目标类型:
let user: User = serde_json::from_str(json_text)?;
也可以使用泛型参数:
let user = serde_json::from_str::<User>(json_text)?;
from_str 会检查 JSON 结构是否符合目标 Rust 类型。如果字段缺失、类型不匹配或数值超出目标类型范围,就会返回错误。 (Docs.rs)
五、为什么推荐使用原始字符串
Rust 普通字符串中的双引号需要转义:
let json_text = "{\"id\":1001,\"username\":\"zhangsan\"}";
这样非常难读。
建议使用 Rust 原始字符串:
let json_text = r#"
{
"id": 1001,
"username": "zhangsan"
}
"#;
原始字符串的基本格式是:
r#"内容"#
里面的双引号不需要写成:
\"
六、常用函数总结
| 函数 | 作用 | 返回类型 |
|---|---|---|
to_string |
Rust 数据转紧凑 JSON 字符串 | String |
to_string_pretty |
Rust 数据转格式化 JSON 字符串 | String |
to_vec |
Rust 数据转 JSON 字节数组 | Vec<u8> |
to_vec_pretty |
Rust 数据转格式化 JSON 字节数组 | Vec<u8> |
to_writer |
将 JSON 写入 Write |
() |
to_writer_pretty |
格式化后写入 Write |
() |
from_str |
从字符串读取 JSON | 指定类型 |
from_slice |
从字节切片读取 JSON | 指定类型 |
from_reader |
从文件、网络流等读取 JSON | 指定类型 |
to_value |
Rust 数据转 Value |
Value |
from_value |
Value 转 Rust 数据 |
指定类型 |
这些函数构成了 serde_json 最主要的高级 API。 (Docs.rs)
七、使用 serde_json::Value 处理动态 JSON
有些 JSON 的结构并不固定,或者只需要读取其中几个字段,此时不一定要定义结构体,可以使用:
serde_json::Value
Value 可以表示任何合法 JSON 值。 (Docs.rs)
1. Value 与 JSON 类型的对应关系
serde_json::Value 大致包含以下类型:
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
对应关系:
| JSON 类型 | Rust Value |
|---|---|
null |
Value::Null |
true、false |
Value::Bool |
| 数字 | Value::Number |
| 字符串 | Value::String |
| 数组 | Value::Array |
| 对象 | Value::Object |
2. JSON 字符串转换为 Value
use serde_json::Value;
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"code": 200,
"message": "success",
"data": {
"id": 1001,
"username": "zhangsan",
"active": true,
"roles": ["admin", "operator"]
}
}
"#;
let value: Value = serde_json::from_str(json_text)?;
println!("状态码:{}", value["code"]);
println!("用户名:{}", value["data"]["username"]);
println!("第一个角色:{}", value["data"]["roles"][0]);
Ok(())
}
输出:
状态码:200 用户名:"zhangsan" 第一个角色:"admin"
注意:直接打印 Value::String 时会保留 JSON 双引号。
3. 将 Value 转换成具体类型
let code = value["code"].as_u64(); let username = value["data"]["username"].as_str(); let active = value["data"]["active"].as_bool();
完整示例:
use serde_json::Value;
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"active": true
}
"#;
let value: Value = serde_json::from_str(json_text)?;
let id = value["id"]
.as_u64()
.ok_or_else(|| {
serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"id 不是 u64",
))
})?;
let username = value["username"]
.as_str()
.unwrap_or("未知用户");
let active = value["active"]
.as_bool()
.unwrap_or(false);
println!("id={id}");
println!("username={username}");
println!("active={active}");
Ok(())
}
不过实际业务中,不建议为了构造业务错误而手动生成 serde_json::Error。更常见的做法是使用自己的错误类型、anyhow 或 thiserror。
4. 推荐使用 get 安全读取
下面这种写法很方便:
value["data"]["username"]
但当字段不存在时,索引操作通常返回:
Value::Null
这可能掩盖“字段不存在”的问题。
更稳妥的写法:
let username = value
.get("data")
.and_then(|data| data.get("username"))
.and_then(Value::as_str);
完整示例:
use serde_json::Value;
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"data": {
"username": "zhangsan"
}
}
"#;
let value: Value = serde_json::from_str(json_text)?;
match value
.get("data")
.and_then(|data| data.get("username"))
.and_then(Value::as_str)
{
Some(username) => println!("用户名:{username}"),
None => println!("用户名不存在或类型不正确"),
}
Ok(())
}
get 在字段不存在、数组越界或类型不符合时返回 None;方括号索引则会在这些情况下返回 Value::Null。 (Docs.rs)
八、使用 json! 宏创建 JSON
serde_json 提供了非常方便的:
json!()
宏。
1. 创建 JSON 对象
use serde_json::json;
fn main() {
let value = json!({
"code": 200,
"message": "success",
"data": {
"id": 1001,
"username": "zhangsan",
"active": true
}
});
println!("{value}");
}
输出:
{"code":200,"data":{"active":true,"id":1001,"username":"zhangsan"},"message":"success"}
json! 的返回类型是:
serde_json::Value
官方文档说明,json! 可以用接近原生 JSON 的语法构建 Value,并支持插入 Rust 变量或表达式。 (Docs.rs)
2. 插入 Rust 变量
use serde_json::json;
fn main() {
let user_id = 1001;
let username = "zhangsan";
let roles = vec!["admin", "operator"];
let value = json!({
"id": user_id,
"username": username,
"roles": roles,
"enabled": true
});
println!("{value}");
}
3. 使用表达式
use serde_json::json;
fn main() {
let page = 2;
let page_size = 20;
let value = json!({
"page": page,
"pageSize": page_size,
"offset": (page - 1) * page_size,
"hasNext": true
});
println!("{value}");
}
九、修改动态 JSON
1. 修改已有字段
use serde_json::json;
fn main() {
let mut value = json!({
"id": 1001,
"username": "zhangsan",
"active": true
});
value["username"] = json!("lisi");
value["active"] = json!(false);
println!("{value}");
}
结果:
{"active":false,"id":1001,"username":"lisi"}
2. 添加字段
use serde_json::json;
fn main() {
let mut value = json!({
"id": 1001
});
value["username"] = json!("zhangsan");
value["roles"] = json!(["admin", "operator"]);
println!("{value}");
}
3. 操作对象 Map
use serde_json::{json, Value};
fn main() {
let mut value = json!({
"id": 1001,
"username": "zhangsan"
});
if let Value::Object(object) = &mut value {
object.insert("active".to_string(), json!(true));
object.remove("username");
}
println!("{value}");
}
4. 操作数组
use serde_json::{json, Value};
fn main() {
let mut value = json!({
"roles": ["admin", "operator"]
});
if let Some(Value::Array(roles)) = value.get_mut("roles") {
roles.push(json!("auditor"));
}
println!("{value}");
}
输出:
{"roles":["admin","operator","auditor"]}
十、Serde 字段属性详解
Serde 提供了大量属性,用于控制字段名称、默认值、忽略规则等。 (Serde)
1. rename:修改 JSON 字段名称
Rust 通常使用蛇形命名:
user_name
而 JSON API 可能使用:
userName
可以这样处理:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
#[serde(rename = "userName")]
user_name: String,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
user_name: "zhangsan".to_string(),
};
println!("{}", serde_json::to_string_pretty(&user)?);
Ok(())
}
输出:
{
"id": 1001,
"userName": "zhangsan"
}
2. rename_all:统一修改所有字段
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct UserProfile {
user_id: u64,
user_name: String,
login_count: u32,
}
fn main() -> Result<(), serde_json::Error> {
let profile = UserProfile {
user_id: 1001,
user_name: "zhangsan".to_string(),
login_count: 5,
};
println!("{}", serde_json::to_string_pretty(&profile)?);
Ok(())
}
输出:
{
"userId": 1001,
"userName": "zhangsan",
"loginCount": 5
}
常用命名规则:
#[serde(rename_all = "camelCase")] #[serde(rename_all = "snake_case")] #[serde(rename_all = "PascalCase")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "kebab-case")]
3. alias:兼容旧字段名称
假设旧接口返回:
{
"nickname": "zhangsan"
}
新接口返回:
{
"displayName": "zhangsan"
}
可以同时兼容:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
#[serde(rename = "displayName", alias = "nickname")]
display_name: String,
}
fn main() -> Result<(), serde_json::Error> {
let old_json = r#"{"nickname":"zhangsan"}"#;
let new_json = r#"{"displayName":"lisi"}"#;
let old_user: User = serde_json::from_str(old_json)?;
let new_user: User = serde_json::from_str(new_json)?;
println!("{old_user:?}");
println!("{new_user:?}");
Ok(())
}
4. default:字段缺失时使用默认值
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
host: String,
#[serde(default)]
debug: bool,
#[serde(default)]
port: u16,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"host": "127.0.0.1"
}
"#;
let config: Config = serde_json::from_str(json_text)?;
println!("{config:#?}");
Ok(())
}
结果:
Config {
host: "127.0.0.1",
debug: false,
port: 0,
}
因为:
bool::default() == false u16::default() == 0 String::default() == "" Vec::default() == [] Option::default() == None
5. 自定义默认值
use serde::Deserialize;
fn default_host() -> String {
"127.0.0.1".to_string()
}
fn default_port() -> u16 {
8080
}
#[derive(Debug, Deserialize)]
struct Config {
#[serde(default = "default_host")]
host: String,
#[serde(default = "default_port")]
port: u16,
}
fn main() -> Result<(), serde_json::Error> {
let config: Config = serde_json::from_str("{}")?;
println!("{config:#?}");
Ok(())
}
输出:
Config {
host: "127.0.0.1",
port: 8080,
}
Serde 支持通过 #[serde(default)] 使用 Default::default(),也支持通过 #[serde(default = "函数名")] 调用指定函数生成默认值。 (Serde)
6. skip_serializing:序列化时忽略字段
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
#[serde(skip_serializing)]
password: String,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
password: "123456".to_string(),
};
println!("{}", serde_json::to_string_pretty(&user)?);
Ok(())
}
输出:
{
"id": 1001,
"username": "zhangsan"
}
但是要注意:
#[serde(skip_serializing)]
只在序列化时忽略,反序列化时仍然会尝试读取该字段。官方建议,如果序列化和反序列化都需要忽略,应使用 skip。 (Serde)
7. skip:序列化和反序列化都忽略
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
#[serde(skip)]
internal_token: String,
}
反序列化时,internal_token 会使用其默认值:
String::default()
也就是空字符串。
8. skip_serializing_if:满足条件时忽略
这是处理 Option 最常见的写法:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
email: None,
};
println!("{}", serde_json::to_string_pretty(&user)?);
Ok(())
}
输出:
{
"id": 1001,
"username": "zhangsan"
}
如果不加这个属性,默认输出:
{
"id": 1001,
"username": "zhangsan",
"email": null
}
十一、Option<T> 处理可选字段
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
email: Option<String>,
}
fn main() -> Result<(), serde_json::Error> {
let json1 = r#"
{
"id": 1001,
"email": "user@example.com"
}
"#;
let json2 = r#"
{
"id": 1002,
"email": null
}
"#;
let json3 = r#"
{
"id": 1003
}
"#;
let user1: User = serde_json::from_str(json1)?;
let user2: User = serde_json::from_str(json2)?;
let user3: User = serde_json::from_str(json3)?;
println!("{user1:?}");
println!("{user2:?}");
println!("{user3:?}");
Ok(())
}
结果:
User { id: 1001, email: Some("user@example.com") }
User { id: 1002, email: None }
User { id: 1003, email: None }
对于普通的 Option<T>:
- 字段存在且有值:
Some(value) - 字段值为
null:None - 字段不存在:通常也是
None
十二、嵌套结构体
实际 API 中经常存在嵌套 JSON。
JSON:
{
"id": 1001,
"username": "zhangsan",
"department": {
"id": 10,
"name": "IT运维部"
}
}
Rust:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Department {
id: u64,
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
department: Department,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"department": {
"id": 10,
"name": "IT运维部"
}
}
"#;
let user: User = serde_json::from_str(json_text)?;
println!("用户:{}", user.username);
println!("部门:{}", user.department.name);
Ok(())
}
十三、JSON 数组
1. 数组转换为 Vec<T>
JSON:
[
{
"id": 1001,
"username": "zhangsan"
},
{
"id": 1002,
"username": "lisi"
}
]
Rust:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
[
{
"id": 1001,
"username": "zhangsan"
},
{
"id": 1002,
"username": "lisi"
}
]
"#;
let users: Vec<User> = serde_json::from_str(json_text)?;
for user in users {
println!("{}:{}", user.id, user.username);
}
Ok(())
}
2. 结构体中包含数组
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
roles: Vec<String>,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"roles": ["admin", "operator"]
}
"#;
let user: User = serde_json::from_str(json_text)?;
for role in user.roles {
println!("角色:{role}");
}
Ok(())
}
十四、处理 HashMap
JSON 对象可以反序列化为:
HashMap<String, T>
示例:
use std::collections::HashMap;
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"host": "127.0.0.1",
"port": "8080",
"environment": "production"
}
"#;
let config: HashMap<String, String> =
serde_json::from_str(json_text)?;
println!("host={:?}", config.get("host"));
println!("port={:?}", config.get("port"));
Ok(())
}
也可以序列化:
use std::collections::HashMap;
fn main() -> Result<(), serde_json::Error> {
let mut map = HashMap::new();
map.insert("host", "127.0.0.1");
map.insert("port", "8080");
let json_text = serde_json::to_string_pretty(&map)?;
println!("{json_text}");
Ok(())
}
JSON 对象的键本质上必须是字符串。对于复杂的非字符串 Map 键,序列化可能失败。serde_json 的序列化函数文档也特别列出了非字符串 Map 键这一错误场景。 (Docs.rs)
十五、使用 flatten 展开字段
1. 展开嵌套结构体
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Pagination {
page: u32,
page_size: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
code: u16,
message: String,
#[serde(flatten)]
pagination: Pagination,
}
fn main() -> Result<(), serde_json::Error> {
let response = ApiResponse {
code: 200,
message: "success".to_string(),
pagination: Pagination {
page: 1,
page_size: 20,
},
};
println!("{}", serde_json::to_string_pretty(&response)?);
Ok(())
}
输出:
{
"code": 200,
"message": "success",
"page": 1,
"page_size": 20
}
没有 flatten 时会变成:
{
"code": 200,
"message": "success",
"pagination": {
"page": 1,
"page_size": 20
}
}
2. 捕获未知字段
use std::collections::HashMap;
use serde::Deserialize;
use serde_json::Value;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
#[serde(flatten)]
extra: HashMap<String, Value>,
}
fn main() -> Result<(), serde_json::Error> {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"department": "IT",
"loginCount": 20,
"enabled": true
}
"#;
let user: User = serde_json::from_str(json_text)?;
println!("id={}", user.id);
println!("username={}", user.username);
println!("其他字段={:#?}", user.extra);
Ok(())
}
flatten 可以将结构体或 Map 的字段内联到父级,也可以用 Map 收集剩余字段。但它不能和同一结构中的 deny_unknown_fields 一起使用。 (Serde)
十六、拒绝未知字段
默认情况下,多余的 JSON 字段通常会被忽略:
{
"id": 1001,
"username": "zhangsan",
"unknownField": "abc"
}
如果希望出现未知字段时直接报错,可以使用:
#[serde(deny_unknown_fields)]
示例:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct User {
id: u64,
username: String,
}
fn main() {
let json_text = r#"
{
"id": 1001,
"username": "zhangsan",
"unknownField": "abc"
}
"#;
match serde_json::from_str::<User>(json_text) {
Ok(user) => println!("{user:?}"),
Err(error) => println!("解析失败:{error}"),
}
}
这适合:
- 严格配置文件
- 内部协议
- 对数据结构要求严格的系统
- 防止前端或第三方传入拼写错误字段
deny_unknown_fields 会在反序列化遇到未知字段时返回错误。 (Serde)
十七、枚举与 JSON
1. 默认的外部标签形式
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
enum UserStatus {
Enabled,
Disabled,
Locked,
}
fn main() -> Result<(), serde_json::Error> {
let status = UserStatus::Enabled;
println!("{}", serde_json::to_string(&status)?);
Ok(())
}
输出:
"Enabled"
带数据的枚举:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
enum Event {
UserCreated {
user_id: u64,
username: String,
},
UserDeleted {
user_id: u64,
},
}
fn main() -> Result<(), serde_json::Error> {
let event = Event::UserCreated {
user_id: 1001,
username: "zhangsan".to_string(),
};
println!("{}", serde_json::to_string_pretty(&event)?);
Ok(())
}
默认输出:
{
"UserCreated": {
"user_id": 1001,
"username": "zhangsan"
}
}
这叫外部标签:
externally tagged
2. 内部标签形式
API 中更常见的是:
{
"type": "user_created",
"userId": 1001,
"username": "zhangsan"
}
可以这样定义:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(
tag = "type",
rename_all = "snake_case",
rename_all_fields = "camelCase"
)]
enum Event {
UserCreated {
user_id: u64,
username: String,
},
UserDeleted {
user_id: u64,
},
Heartbeat,
}
fn main() -> Result<(), serde_json::Error> {
let event = Event::UserCreated {
user_id: 1001,
username: "zhangsan".to_string(),
};
println!("{}", serde_json::to_string_pretty(&event)?);
Ok(())
}
输出类似:
{
"type": "user_created",
"userId": 1001,
"username": "zhangsan"
}
内部标签通过:
#[serde(tag = "type")]
实现。
3. 相邻标签形式
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
enum Event {
UserCreated {
user_id: u64,
},
Message(String),
}
fn main() -> Result<(), serde_json::Error> {
let event = Event::Message("系统维护".to_string());
println!("{}", serde_json::to_string_pretty(&event)?);
Ok(())
}
输出:
{
"type": "Message",
"data": "系统维护"
}
4. untagged:无标签枚举
有些接口中的 ID 可能是数字,也可能是字符串:
1001
或者:
"1001"
可以使用:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum UserId {
Number(u64),
Text(String),
}
fn main() -> Result<(), serde_json::Error> {
let id1: UserId = serde_json::from_str("1001")?;
let id2: UserId = serde_json::from_str(r#""U1001""#)?;
println!("{id1:?}");
println!("{id2:?}");
Ok(())
}
结果:
Number(1001)
Text("U1001")
对于 untagged,Serde 会按照枚举变体声明顺序尝试反序列化,首个成功匹配的变体会被采用。因此,多个结构相似的变体需要注意排列顺序。Serde 一共支持外部标签、内部标签、相邻标签和无标签四种主要枚举表示方式。 (Serde)
十八、Rust 数据与 Value 相互转换
1. 结构体转换为 Value
use serde::Serialize;
use serde_json::Value;
#[derive(Debug, Serialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
};
let value: Value = serde_json::to_value(&user)?;
println!("{value}");
Ok(())
}
2. Value 转换为结构体
use serde::Deserialize;
use serde_json::json;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let value = json!({
"id": 1001,
"username": "zhangsan"
});
let user: User = serde_json::from_value(value)?;
println!("{user:?}");
Ok(())
}
注意:
from_value(value)
会取得 value 的所有权。
如果后面还要继续使用原来的 Value,需要:
let user: User = serde_json::from_value(value.clone())?;
to_value 和 from_value 专门用于 Rust 类型和动态 Value 之间的转换。 (Docs.rs)
十九、读取 JSON 文件
假设有文件:
config.json
内容:
{
"host": "127.0.0.1",
"port": 8080,
"debug": true
}
代码:
use std::fs::File;
use std::io::BufReader;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
host: String,
port: u16,
debug: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("config.json")?;
let reader = BufReader::new(file);
let config: Config = serde_json::from_reader(reader)?;
println!("{config:#?}");
Ok(())
}
处理文件时推荐:
BufReader<File>
因为 serde_json::from_reader 本身不会自动为输入增加缓冲。官方文档也建议对 File 等数据源自行使用缓冲。 (Docs.rs)
二十、将 JSON 写入文件
use std::fs::File;
use std::io::BufWriter;
use serde::Serialize;
#[derive(Debug, Serialize)]
struct Config {
host: String,
port: u16,
debug: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config {
host: "127.0.0.1".to_string(),
port: 8080,
debug: true,
};
let file = File::create("config.json")?;
let writer = BufWriter::new(file);
serde_json::to_writer_pretty(writer, &config)?;
Ok(())
}
写入后的文件:
{
"host": "127.0.0.1",
"port": 8080,
"debug": true
}
to_writer 写入紧凑 JSON,to_writer_pretty 写入格式化 JSON。两者都接受实现了 std::io::Write 的目标。 (Docs.rs)
二十一、处理字节数组
网络请求、消息队列、文件读取中,JSON 经常是:
&[u8]
1. 字节数组转结构体
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let bytes = br#"
{
"id": 1001,
"username": "zhangsan"
}
"#;
let user: User = serde_json::from_slice(bytes)?;
println!("{user:?}");
Ok(())
}
2. 结构体转字节数组
use serde::Serialize;
#[derive(Debug, Serialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1001,
username: "zhangsan".to_string(),
};
let bytes: Vec<u8> = serde_json::to_vec(&user)?;
println!("{bytes:?}");
let text = String::from_utf8_lossy(&bytes);
println!("{text}");
Ok(())
}
from_slice 从 JSON 字节切片反序列化,to_vec 则序列化为 UTF-8 JSON 字节数组。 (Docs.rs)
二十二、通用 API 响应结构
实际 Web 项目中,经常会定义:
{
"code": 200,
"message": "success",
"data": {
"id": 1001,
"username": "zhangsan"
}
}
可以使用泛型:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse<T> {
code: u16,
message: String,
data: T,
}
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
}
fn main() -> Result<(), serde_json::Error> {
let response = ApiResponse {
code: 200,
message: "success".to_string(),
data: User {
id: 1001,
username: "zhangsan".to_string(),
},
};
let json_text = serde_json::to_string_pretty(&response)?;
println!("{json_text}");
let parsed: ApiResponse<User> =
serde_json::from_str(&json_text)?;
println!("{parsed:#?}");
Ok(())
}
列表接口:
type UserListResponse = ApiResponse<Vec<User>>;
例如:
let response: ApiResponse<Vec<User>> =
serde_json::from_str(json_text)?;
二十三、固定字段加动态字段
有时候 API 的基础结构固定,但 data 不固定:
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
code: u16,
message: String,
data: Value,
}
使用:
use serde_json::json;
fn main() -> Result<(), serde_json::Error> {
let response = ApiResponse {
code: 200,
message: "success".to_string(),
data: json!({
"id": 1001,
"username": "zhangsan"
}),
};
println!("{}", serde_json::to_string_pretty(&response)?);
Ok(())
}
这种方式适合:
- 网关
- 日志系统
- 消息转发
- 数据结构尚未确定
- 接口响应类型很多
但是普通业务代码中,能够定义具体结构体时,应优先使用具体结构体,因为它有:
- 编译期类型检查
- 更清晰的字段含义
- 更好的 IDE 提示
- 更容易重构
- 更少的运行时错误
二十四、错误处理
不推荐在正式业务代码中到处使用:
unwrap()
例如:
let user: User = serde_json::from_str(json_text).unwrap();
JSON 一旦格式错误,程序就会 panic。
推荐使用:
?
或者显式处理错误。
1. 使用 match
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct User {
id: u64,
username: String,
}
fn main() {
let json_text = r#"
{
"id": "不是数字",
"username": "zhangsan"
}
"#;
match serde_json::from_str::<User>(json_text) {
Ok(user) => {
println!("解析成功:{user:?}");
}
Err(error) => {
println!("解析失败:{error}");
println!("错误行:{}", error.line());
println!("错误列:{}", error.column());
println!("错误类别:{:?}", error.classify());
}
}
}
2. 错误分类
serde_json::Error 可以分为:
Category::Io Category::Syntax Category::Data Category::Eof
含义:
| 类型 | 含义 |
|---|---|
Io |
文件、网络流等读写失败 |
Syntax |
JSON 语法错误 |
Data |
JSON 合法,但与 Rust 类型不匹配 |
Eof |
JSON 数据意外结束 |
例如:
use serde_json::error::Category;
fn print_json_error(error: &serde_json::Error) {
match error.classify() {
Category::Io => {
println!("JSON 输入输出错误");
}
Category::Syntax => {
println!(
"JSON 语法错误,位置:{}:{}",
error.line(),
error.column()
);
}
Category::Data => {
println!(
"JSON 数据类型不匹配,位置:{}:{}",
error.line(),
error.column()
);
}
Category::Eof => {
println!("JSON 内容不完整");
}
}
println!("详细错误:{error}");
}
serde_json::Error 官方提供了 line、column、classify、is_syntax、is_data、is_io 和 is_eof 等方法。 (Docs.rs)
二十五、语法错误和数据错误的区别
1. Syntax:JSON 本身不合法
{
"id": 1001,
"username": "zhangsan",
}
最后一个字段后面多了逗号。
或者:
{
'id': 1001
}
JSON 字段名使用了单引号。
这些属于 JSON 语法错误。
2. Data:JSON 合法,但类型不匹配
结构体:
struct User {
id: u64,
}
JSON:
{
"id": "1001"
}
JSON 本身合法,但 "1001" 是字符串,而结构体要求 u64,因此属于数据类型错误。
二十六、处理字段可能是字符串或数字
第三方接口可能返回:
{
"id": 1001
}
也可能返回:
{
"id": "1001"
}
可以使用无标签枚举:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum NumberOrString {
Number(u64),
String(String),
}
#[derive(Debug, Deserialize)]
struct User {
id: NumberOrString,
}
impl NumberOrString {
fn into_u64(self) -> Result<u64, String> {
match self {
Self::Number(value) => Ok(value),
Self::String(value) => value
.parse::<u64>()
.map_err(|error| format!("无效数字:{error}")),
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json1 = r#"{"id":1001}"#;
let json2 = r#"{"id":"1002"}"#;
let user1: User = serde_json::from_str(json1)?;
let user2: User = serde_json::from_str(json2)?;
println!("id1={}", user1.id.into_u64()?);
println!("id2={}", user2.id.into_u64()?);
Ok(())
}
二十七、连续读取多个 JSON 对象
对于日志或 JSON 流:
{"id":1,"message":"start"}
{"id":2,"message":"running"}
{"id":3,"message":"finished"}
可以使用:
serde_json::Deserializer
示例:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct LogEntry {
id: u64,
message: String,
}
fn main() {
let input = r#"
{"id":1,"message":"start"}
{"id":2,"message":"running"}
{"id":3,"message":"finished"}
"#;
let stream =
serde_json::Deserializer::from_str(input)
.into_iter::<LogEntry>();
for item in stream {
match item {
Ok(log) => println!("{log:?}"),
Err(error) => {
eprintln!("解析失败:{error}");
break;
}
}
}
}
serde_json::StreamDeserializer 可以将输入流中的多个连续 JSON 值作为迭代器逐个反序列化。 (Docs.rs)
二十八、一个较完整的配置文件案例
config.json:
{
"server": {
"host": "0.0.0.0",
"port": 8080
},
"database": {
"url": "mysql://root:password@127.0.0.1/app",
"maxConnections": 20
},
"features": {
"enableAudit": true,
"enableRegistration": false
},
"logLevel": "info"
}
Rust:
use std::fs::File;
use std::io::BufReader;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
features: FeatureConfig,
#[serde(default = "default_log_level")]
log_level: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DatabaseConfig {
url: String,
max_connections: u32,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeatureConfig {
#[serde(default)]
enable_audit: bool,
#[serde(default)]
enable_registration: bool,
}
fn default_log_level() -> String {
"info".to_string()
}
fn load_config(
path: &str,
) -> Result<AppConfig, Box<dyn std::error::Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let config = serde_json::from_reader(reader)?;
Ok(config)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = load_config("config.json")?;
println!("监听地址:{}", config.server.host);
println!("监听端口:{}", config.server.port);
println!("数据库:{}", config.database.url);
println!(
"最大连接数:{}",
config.database.max_connections
);
println!("审计功能:{}", config.features.enable_audit);
println!("日志级别:{}", config.log_level);
Ok(())
}
二十九、结构体还是 Value,应该怎么选
优先使用结构体的场景
#[derive(Serialize, Deserialize)]
struct User {
id: u64,
username: String,
}
适合:
- JSON 结构明确
- 内部业务系统
- API 请求参数
- API 响应对象
- 配置文件
- 数据库扩展 JSON 字段有固定结构
优点:
- 类型安全
- IDE 自动提示
- 编译时发现部分问题
- 字段含义明确
- 容易维护
使用 Value 的场景
let value: serde_json::Value =
serde_json::from_str(json_text)?;
适合:
- JSON 结构完全动态
- 只读取少量字段
- API 网关透传
- 消息转发
- 日志采集
- 接收未知第三方数据
- 某些字段结构不固定
混合使用
#[derive(Deserialize)]
struct ApiRequest {
request_id: String,
action: String,
data: serde_json::Value,
}
这是实际项目中很常见的折中方案:
- 外层协议结构固定;
data根据action动态变化。
三十、常见问题与踩坑点
1. JSON 必须使用双引号
错误:
{
'name': 'zhangsan'
}
正确:
{
"name": "zhangsan"
}
2. JSON 不允许尾随逗号
错误:
{
"id": 1001,
"name": "zhangsan",
}
正确:
{
"id": 1001,
"name": "zhangsan"
}
3. 必填字段缺失会报错
结构体:
struct User {
id: u64,
username: String,
}
JSON:
{
"id": 1001
}
会报类似:
missing field `username`
解决方式:
#[serde(default)] username: String
或者:
username: Option<String>
4. null 不能直接转换为普通类型
JSON:
{
"email": null
}
错误定义:
email: String
推荐:
email: Option<String>
5. 字符串数字不能自动变成数字
JSON:
{
"id": "1001"
}
结构体:
id: u64
默认不会自动转换。
需要:
- 修改结构体为
String; - 使用
#[serde(untagged)]; - 或实现自定义反序列化。
6. 不要过度依赖方括号索引
value["data"]["user"]["name"]
字段缺失时可能得到 Value::Null,而不是马上报错。
重要业务字段推荐使用:
value
.get("data")
.and_then(|value| value.get("user"))
.and_then(|value| value.get("name"))
.and_then(Value::as_str)
或者直接定义结构体。
7. 不要在业务代码中大量使用 unwrap
不推荐:
let user: User = serde_json::from_str(json).unwrap();
推荐:
let user: User = serde_json::from_str(json)?;
或者:
match serde_json::from_str::<User>(json) {
Ok(user) => println!("{user:?}"),
Err(error) => eprintln!("JSON 解析失败:{error}"),
}
8. JSON 对象字段顺序不应作为业务逻辑
JSON 对象在业务语义上应被视为键值集合,不应依赖字段输出顺序。
serde_json::Value::Object 默认使用排序 Map;开启:
serde_json = {
version = "1.0",
features = ["preserve_order"]
}
后可使用保持插入顺序的实现。 (Docs.rs)
三十一、推荐的项目使用方式
对于正常的 Rust Web 项目,建议按下面的方式使用。
请求参数
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateUserRequest {
username: String,
email: Option<String>,
role_ids: Vec<u64>,
}
响应数据
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct UserResponse {
id: u64,
username: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
}
通用响应结构
#[derive(Debug, Serialize)]
struct ApiResponse<T> {
code: u16,
message: String,
data: T,
}
动态扩展字段
use std::collections::HashMap;
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
struct Asset {
id: u64,
name: String,
#[serde(default)]
attributes: HashMap<String, Value>,
}
三十二、最终记忆表
最常用的几个操作可以记成:
// Rust 数据 -> JSON 字符串
let json = serde_json::to_string(&value)?;
// Rust 数据 -> 格式化 JSON 字符串
let json = serde_json::to_string_pretty(&value)?;
// JSON 字符串 -> Rust 结构体
let value: User = serde_json::from_str(json_text)?;
// JSON 字节 -> Rust 结构体
let value: User = serde_json::from_slice(bytes)?;
// 文件、网络流 -> Rust 结构体
let value: User = serde_json::from_reader(reader)?;
// 创建动态 JSON
let value = serde_json::json!({
"code": 200,
"message": "success"
});
// Rust 数据 -> 动态 Value
let value = serde_json::to_value(&user)?;
// 动态 Value -> Rust 数据
let user: User = serde_json::from_value(value)?;
核心选择原则是:
JSON 结构固定
→ 使用 struct + Serialize/Deserialize
JSON 结构不固定
→ 使用 serde_json::Value
外层固定、内部动态
→ struct + Value
需要容错字段
→ Option、default、alias
需要严格校验
→ deny_unknown_fields