Rust序列化与反序列化-Serde 开发指南:从入门到实战
Serde 是 Rust 生态中处理数据序列化与反序列化的强有力工具,它能轻松将 Rust 数据结构转换成 JSON、YAML、Web Form 等多种格式,也能将这些格式的数据解析回 Rust 类型。本文将用通俗的语言和丰富的例子,带你从基础到实战,彻底掌握 Serde 的使用。
如果您能掌握Serde,就能极大提升开发效率。核心要点:
- 基础用法:通过
#[derive(Serialize, Deserialize)]
快速获得序列化 / 反序列化能力。 - 格式支持:JSON 用于通用场景,Web Form 用于表单 / URL 参数,其他格式按需选择。
- 定制化:用
rename
、default
等属性解决字段名、默认值等问题。 - 进阶能力:复杂转换场景下,手动实现 trait 控制序列化 / 反序列化逻辑。
一、准备工作:安装依赖
在开始前,需要在 Cargo.toml
中添加 Serde 及对应的数据格式库。以最常用的 JSON 和 Web Form 格式为例:
[dependencies]
# 核心库:提供序列化/反序列化能力,启用derive特性可自动生成代码
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" # 处理JSON格式
serde_urlencoded = "0.7" # 处理Web Form格式(application/x-www-form-urlencoded)
- serde:核心库,定义了
Serialize
(序列化)和Deserialize
(反序列化)两个核心 trait。 - serde_json:基于 Serde 实现 JSON 格式的转换。
- serde_urlencoded:处理 Web 表单常用的
application/x-www-form-urlencoded
格式(比如 HTML 表单提交的数据)。
二、核心概念:序列化与反序列化
简单说:
- 序列化:把 Rust 中的数据结构(如结构体、枚举)转换成字符串或字节流(比如 JSON 字符串、Form 表单字符串)。
类比:把行李打包成快递箱,方便运输。 - 反序列化:把字符串或字节流解析回 Rust 数据结构。
类比:收到快递后,把箱子里的东西拿出来还原成原来的样子。
Serde 的核心是两个 trait:
Serialize
:类型需要实现此 trait 才能被序列化。Deserialize
:类型需要实现此 trait 才能被反序列化。
99% 的场景下,无需手动实现这两个 trait,只需在类型上添加 #[derive(Serialize, Deserialize)]
即可自动生成代码。
三、基础实战:序列化与反序列化
1. 结构体与 JSON 格式
先从最常用的 JSON 格式开始,定义一个 User
结构体,演示完整流程:
use serde::{Serialize, Deserialize};
use serde_json;// 派生序列化和反序列化能力,同时派生Debug用于打印,PartialEq用于比较
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct User {name: String,age: u32,is_active: bool,hobbies: Vec<String>, // 数组类型
}fn main() -> Result<(), Box<dyn std::error::Error>> {// 1. 创建一个Rust数据实例let user = User {name: "张三".to_string(),age: 28,is_active: true,hobbies: vec!["读书".to_string(), "跑步".to_string()],};// 2. 序列化:Rust数据 → JSON字符串let json_str = serde_json::to_string(&user)?;println!("JSON序列化结果:\n{}", json_str);// 输出:{"name":"张三","age":28,"is_active":true,"hobbies":["读书","跑步"]}// 3. 反序列化:JSON字符串 → Rust数据let parsed_user: User = serde_json::from_str(&json_str)?;println!("\n反序列化结果:{:?}", parsed_user);// 输出:User { name: "张三", age: 28, is_active: true, hobbies: ["读书", "跑步"] }// 验证反序列化后的数据和原数据一致assert_eq!(user, parsed_user);Ok(())
}
执行结果:
JSON序列化结果:
{"name":"张三","age":28,"is_active":true,"hobbies":["读书","跑步"]}反序列化结果:User { name: "张三", age: 28, is_active: true, hobbies: ["读书", "跑步"] }
关键说明:
serde_json::to_string
:序列化函数,返回Result<String, Error>
。serde_json::from_str
:反序列化函数,需要指定目标类型(如User
)。?
操作符:自动处理错误(如果序列化 / 反序列化失败,会返回错误)。
2. 枚举与 JSON 格式
枚举的序列化需要特别处理(因为要区分不同的枚举变体)。通过 #[serde(tag = "type")]
可以在 JSON 中添加一个标记字段(如 type
)来区分变体:
use serde::{Serialize, Deserialize};
use serde_json;#[derive(Debug, Serialize, Deserialize, PartialEq)]
// 用"type"字段标记枚举变体(JSON中会多一个"type"字段)
#[serde(tag = "type")]
enum Message {Text { content: String }, // 文本消息:包含content字段Image { url: String, size: u32 }, // 图片消息:包含url和size
}fn main() -> Result<(), Box<dyn std::error::Error>> {// 创建一个图片消息实例let msg = Message::Image {url: "https://example.com/photo.jpg".to_string(),size: 2048,};// 序列化(用to_string_pretty输出带缩进的格式,方便阅读)let json_str = serde_json::to_string_pretty(&msg)?;println!("枚举序列化结果:\n{}", json_str);// 输出:// {// "type": "Image",// "url": "https://example.com/photo.jpg",// "size": 2048// }// 反序列化let parsed_msg: Message = serde_json::from_str(&json_str)?;assert_eq!(msg, parsed_msg); // 验证结果正确Ok(())
}
执行结果:
枚举序列化结果:
{"type": "Image","url": "https://example.com/photo.jpg","size": 2048
}
3. Web Form 格式(application/x-www-form-urlencoded)
Web Form 格式是 HTML 表单提交数据的默认格式(如 name=张三&age=28
),常用于简单的键值对数据。Serde 中用 serde_urlencoded
库处理这种格式。
特点:
- 数据以键值对形式存在,用
&
分隔(如key1=value1&key2=value2
)。 - 数组通常用重复键表示(如
hobbies=读书&hobbies=跑步
)。
示例:用 Web Form 格式序列化 / 反序列化用户数据:
use serde::{Serialize, Deserialize};
use serde_urlencoded;#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct FormData {username: String,age: u32,interests: String,
}fn main() -> Result<(), Box<dyn std::error::Error>> {// 1. 创建Rust数据实例let data = FormData {username: "李四".to_string(),age: 30,interests: "编程,旅行".to_string(),};// 2. 序列化:Rust数据 → Web Form字符串let form_str = serde_urlencoded::to_string(&data)?;println!("Web Form序列化结果:\n{}", form_str);// 3. 反序列化:Web Form字符串 → Rust数据let parsed_data: FormData = serde_urlencoded::from_str(&form_str)?;println!("\n反序列化结果:{:?}", parsed_data);assert_eq!(data, parsed_data);Ok(())
}
执行结果:
Web Form序列化结果:
username=%E6%9D%8E%E5%9B%9B&age=30&interests=%E7%BC%96%E7%A8%8B%2C%E6%97%85%E8%A1%8C反序列化结果:FormData { username: "李四", age: 30, interests: "编程,旅行" }
应用场景:
- 处理 HTML 表单提交的数据。
- 解析 URL 中的查询参数(如
https://example.com?page=1&size=10
)。 - 与后端 API 进行简单的键值对数据交互。
四、定制化:用属性控制序列化行为
实际开发中,Rust 数据结构的字段名、格式可能和目标数据格式(如 JSON、Form)不匹配,这时可以用 Serde 提供的属性(Attribute)定制行为。
1. 字段重命名
当 Rust 字段名与目标格式的键名不一致时(比如 Rust 用驼峰命名,JSON 用蛇形命名),用 #[serde(rename)]
重命名:
#[derive(Serialize)]
struct Person {#[serde(rename = "full_name")] // JSON中显示为"full_name"name: String,#[serde(rename(serialize = "user_age", deserialize = "age"))]// 序列化时用"user_age",反序列化时用"age"age: u32,
}// 序列化后会得到:{"full_name":"张三","user_age":28}
2. 跳过字段
某些字段可能不需要序列化(如敏感信息)或反序列化(如临时计算的字段),用以下属性控制:
#[derive(Serialize, Deserialize)]
struct UserData {public_info: String, // 正常序列化/反序列化#[serde(skip)] // 既不序列化也不反序列化(全程忽略)password: String,#[serde(skip_serializing)] // 只跳过序列化(反序列化仍会解析)temp_token: String,#[serde(skip_deserializing)] // 只跳过反序列化(序列化仍会输出)server_time: u64,
}
3. 默认值
当反序列化时目标数据缺少某个字段,可以用 #[serde(default)]
设默认值(需实现 Default
trait):
use std::default::Default;#[derive(Serialize, Deserialize)]
struct Config {timeout: u32, // 必须有值,否则反序列化失败#[serde(default)] // 缺失时用u8的默认值0retries: u8,#[serde(default = "default_port")] // 用自定义函数设默认值port: u16,
}// 自定义默认端口函数
fn default_port() -> u16 { 8080 }// 即使JSON中没有"retries"和"port",也能正常反序列化:
// retries默认0,port默认8080
4. 处理 Option 类型
Option<T>
类型表示字段可能存在或不存在。用 #[serde(skip_serializing_if = "Option::is_none")]
可以在值为 None
时跳过序列化:
#[derive(Serialize)]
struct Product {id: u32,name: String,#[serde(skip_serializing_if = "Option::is_none")]// 如果description是None,序列化时不包含这个字段description: Option<String>,
}// 当description为None时,序列化结果:{"id":1,"name":"手机"}
// 当description为Some("新款手机")时:{"id":1,"name":"手机","description":"新款手机"}
五、进阶:手动实现序列化 / 反序列化
自动派生能解决大部分问题,但如果需要复杂的格式转换(如自定义日期格式、特殊编码),就需要手动实现 Serialize
和 Deserialize
trait。
示例:处理自定义日期格式
假设我们需要将 chrono::DateTime
类型序列化为 YYYY-MM-DD
字符串,而非默认格式:
依赖:
[dependencies]
# 核心库:提供序列化/反序列化能力,启用derive特性可自动生成代码
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" # 处理JSON格式
serde_urlencoded = "0.7" # 处理Web Form格式(application/x-www-form-urlencoded)
chrono = "0.4"
代码:
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use chrono::{DateTime, Local, NaiveDate, TimeZone}; // 确保导入了 TimeZonestruct MyDate(DateTime<Local>);impl Serialize for MyDate {fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>whereS: Serializer,{let date_str = self.0.format("%Y-%m-%d").to_string();serializer.serialize_str(&date_str)}
}impl<'de> Deserialize<'de> for MyDate {fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>whereD: Deserializer<'de>,{let date_str: String = Deserialize::deserialize(deserializer)?;let naive_date = NaiveDate::parse_from_str(&date_str, "%Y-%m-%d").map_err(|e| serde::de::Error::custom(format!("日期格式错误: {}", e)))?;let dt = naive_date.and_hms_opt(0, 0, 0).ok_or_else(|| serde::de::Error::custom("无效的时间"))?.and_local_timezone(Local).earliest().ok_or_else(|| serde::de::Error::custom("无效的日期时间"))?;Ok(MyDate(dt))}
}fn main() -> Result<(), Box<dyn std::error::Error>> {let date = MyDate(Local.with_ymd_and_hms(2024, 5, 18, 0, 0, 0).unwrap());let json_str = serde_json::to_string(&date)?;println!("日期序列化结果:{}", json_str);let parsed_date: MyDate = serde_json::from_str(&json_str)?;// let parsed_date: MyDate = serde_json::from_str("\"2024-06-10\"")?;println!("反序列化结果:{}", parsed_date.0.format("%Y-%m-%d"));assert_eq!(date.0, parsed_date.0);Ok(())
}
执行结果:
日期序列化结果:"2024-05-18"
反序列化结果:2024-05-18
关键点:
- 手动实现
Serialize
时,调用serializer
的方法(如serialize_str
)输出目标格式。 - 手动实现
Deserialize
时,通常需要先解析为基础类型(如字符串),再转换为目标类型。
六、错误处理
序列化 / 反序列化可能失败(如格式错误、字段缺失、类型不匹配),Serde 会返回具体的错误信息,我们可以捕获并处理这些错误:
fn parse_user(json_str: &str) -> Result<User, String> {serde_json::from_str(json_str).map_err(|e| {// 自定义错误消息,包含具体错误原因format!("解析用户数据失败:{}", e)})
}// 示例错误场景:
// 1. JSON格式错误(如缺少闭合括号)→ 解析失败
// 2. 字段缺失(如User缺少"name"字段)→ 错误提示"missing field `name`"
// 3. 类型不匹配(如"age"是字符串而非数字)→ 错误提示"invalid type: string"
七、支持的其他数据格式
Serde 生态支持几乎所有主流数据格式,用法与 JSON/Form 类似,只需替换对应的库:
数据格式 | 库名称 | 应用场景 |
---|---|---|
JSON | serde_json | 前后端交互、配置文件 |
YAML | serde_yaml | 配置文件(比 JSON 更易读) |
TOML | toml | Rust 项目的 Cargo.toml 配置 |
CSV | csv | 表格数据(如 Excel 导出文件) |
MessagePack | rmp-serde | 二进制格式,比 JSON 更紧凑 |
Bincode | bincode | Rust 程序间的二进制通信 |
Web Form | serde_urlencoded | 表单提交、URL 查询参数 |