当前位置: 首页 > news >正文

Rust序列化与反序列化-Serde 开发指南:从入门到实战

Serde 是 Rust 生态中处理数据序列化与反序列化的强有力工具,它能轻松将 Rust 数据结构转换成 JSON、YAML、Web Form 等多种格式,也能将这些格式的数据解析回 Rust 类型。本文将用通俗的语言和丰富的例子,带你从基础到实战,彻底掌握 Serde 的使用。

如果您能掌握Serde,就能极大提升开发效率。核心要点:

  1. 基础用法:通过 #[derive(Serialize, Deserialize)] 快速获得序列化 / 反序列化能力。
  2. 格式支持:JSON 用于通用场景,Web Form 用于表单 / URL 参数,其他格式按需选择。
  3. 定制化:用 renamedefault 等属性解决字段名、默认值等问题。
  4. 进阶能力:复杂转换场景下,手动实现 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 类似,只需替换对应的库:

数据格式库名称应用场景
JSONserde_json前后端交互、配置文件
YAMLserde_yaml配置文件(比 JSON 更易读)
TOMLtomlRust 项目的 Cargo.toml 配置
CSVcsv表格数据(如 Excel 导出文件)
MessagePackrmp-serde二进制格式,比 JSON 更紧凑
BincodebincodeRust 程序间的二进制通信
Web Formserde_urlencoded表单提交、URL 查询参数

http://www.dtcms.com/a/361567.html

相关文章:

  • php + docker + idea debug
  • Elasticsearch面试精讲 Day 4:集群发现与节点角色
  • Ubuntu 22.04 装机黑屏(Nvidia显卡) 安装
  • 如何在 vscode 上用 git 将项目 push 到远程仓库 and 常用Git 命令
  • ubuntu 创建系统服务 开机自启
  • 毕业设计:丹麦电力电价预测预测未来24小时的电价pytorch+lstm+历史特征和价格+时间序列 电价预测模型资源 完整代码数据可直接运行
  • 【Node.js教程】Express框架入门:从搭建到动态渲染商品列表
  • 数据结构基础--最小生成树
  • MiniCPM-V 4.5实战,实现图片、视频、多图的推理
  • Python 爬虫实战:爬取 B 站视频的完整教程
  • 【RK3576】【Android14】PMIC电源管理
  • 【学Python自动化】 6.1 Python 模块系统学习笔记 (与 Rust 对照)
  • 数据结构:单链表的应用(力扣算法题)第三章
  • Windows 电脑安装dify
  • Go初级之六:接口(Interface)
  • VBA开发者的福音:让代码效率暴涨300%的终极数据结构选择指南
  • git使用详解和实战示例
  • 【学习笔记】从“两个细则”到“四遥”
  • docker安装redis,进入命令窗口基操练习命令
  • KubeBlocks for Milvus 揭秘
  • 学习 Android (十八) 学习 OpenCV (三)
  • 向量数据库概述:Faiss、Milvus、Qdrant、Chroma、Weaviate
  • AI 时代的用户体验设计:设计师会被替代,还是更值钱?
  • TCP连接状态详解/同时打开Simultaneous Open
  • 动态滑动窗口还搞不清?一文搞定动态滑动窗口 | 基础算法
  • 如何将多个Excel报表合并为一个汇总文件?
  • C++ multiset数据结构的使用情况说明
  • [界面通过zmq请求调用指定动态库函数(二)]不同动态库接口不同
  • Unity游戏打包——打包流程
  • 【开题答辩全过程】以 中华美食宝典食谱分享系统的设计与实现为例,包含答辩的问题和答案