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的默认值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 查询参数

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐