Rust序列化与反序列化-Serde 开发指南:从入门到实战
文章介绍了 Rust 生态的 Serde 工具,包括其定位(数据序列化 / 反序列化工具,支持多格式)、准备工作(Cargo.toml 添加 serde 及对应格式库依赖)、核心概念(序列化 / 反序列化定义,Serialize 和 Deserialize trait 及自动派生);还讲了基础实战(结构体 / 枚举与 JSON、Web Form 的序列化反序列化示例)、定制化方法(字段重命名、跳过
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的默认值0
retries: 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}; // 确保导入了 TimeZone
struct MyDate(DateTime<Local>);
impl Serialize for MyDate {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: 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>
where
D: 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 查询参数 |
更多推荐
所有评论(0)