Rust 序列化库 Serde 完全指南:从入门到实战

0 阅读8分钟

Rust 序列化库 Serde 完全指南:从入门到实战

序列化是贯穿开发的高频场景,比如网络通信的数据传输、配置文件的读写、数据持久化存储、跨服务接口的交互等。Serde 凭借高效、通用、灵活的特性,成为了绝大多数 Rust 项目的首选,甚至已然成为 Rust 生态中事实上的序列化标准库。

为什么 Serde 能成为 Rust 生态事实上的序列化标准

Rust 的核心设计理念之一是“零成本抽象”,Serde 严格遵循这一原则,与其他编程语言的序列化库不同,Serde 通过 Rust 的宏机制在编译期为数据结构自动生成序列化与反序列化代码,既保证了性能,又避免了手动编写重复代码的繁琐。这种非反射、编译期宏展开的机制,让 Serde 在序列化速度上表现非常突出,尤其是在高频序列化的场景,这也是大多数 Rust 项目优先选择 Serde 的核心原因。

除了零成本抽象,Serde 另一个核心设计是“解耦”,将数据结构的序列化能力与具体的序列化格式分离。任何数据只要实现了 Serialize(序列化)和 Deserialize(反序列化)这两个核心 trait,都能被 Serde 处理,无需额外的配置。而且社区提供丰富格式适配器库,如 serde_jsonserde_yamltomlbincode 等,要进行相应格式的序列化与反序列化也非常方便。

也正因为以上两大核心原因,Serde 已成为 Rust 生态中事实上的序列化标准库,大量 Rust 生态库均集成了 Serde,如 Axum、Actix-web、Sqlx、Diesel 等。

快速上手 Serde

添加依赖

Cargo.toml 中添加 Serde 核心库及最常用的 JSON 格式适配器:

[dependencies]
# Serde 核心库,启用 derive 宏,自动生成 trait 实现
serde = { version = "1.0", features = ["derive"] }
# JSON 格式适配器
serde_json = "1.0"

通过 Serde 的 derive 特性,我们通过 Serialize, Deserialize 这两个派生宏,自动为数据结构实现序列化与反序列化的 trait,无需编写代码。

示例代码

Serde 自动支持 Rust 标准库中的大部分类型,如 u32、String、Vec、HashMap、Option 等,无需额外实现 trait,这极大的降低了使用门槛。对于无法不支持的数据类型,我们在高级应用章节会讲解如何处理。

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// 为结构体添加序列化/反序列化能力,Debug 用于打印
#[derive(Serialize, Deserialize, Debug)]
// 使用驼峰命名,如将 is_active 转换为 isActive
#[serde(rename_all = "camelCase")]
struct User {
    id: u32,
    name: String,
    email: Option<String>, // 可选字段,反序列化时缺失则为 None
    #[serde(default)] // 缺失字段默认填充使用该类型默认值(bool 默认为 false)
    is_active: bool,
    tags: Vec<String>,
    metadata: HashMap<String, String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut metadata = HashMap::new();
    metadata.insert("role".to_string(), "admin".to_string());
    metadata.insert("version".to_string(), "1.0.0".to_string());
    let user = User {
        id: 1001,
        name: "Alice".to_string(),
        email: Some("alice@example.com".to_string()),
        is_active: true,
        tags: vec!["rust".to_string(), "serde".to_string()],
        metadata,
    };

    // 序列化
    let json_str = serde_json::to_string(&user)?;
    println!("序列化结果: {}", json_str);

    // 反序列化
    let deserialized_user: User = serde_json::from_str(&json_str)?;
    println!("反序列化结果: {:?}", deserialized_user);

    Ok(())
}

在项目根目录执行 cargo run 可得到如下结果:

序列化结果: {"id":1001,"name":"Alice","email":"alice@example.com","isActive":true,"tags":["rust","serde"],"metadata":{"version":"1.0.0","role":"admin"}}
反序列化结果: User { id: 1001, name: "Alice", email: Some("alice@example.com"), is_active: true, tags: ["rust", "serde"], metadata: {"version": "1.0.0", "role": "admin"} }

进阶技巧:自定义序列化/反序列化

学会了上面的示例,基本就满足大部分的使用场景了。但是在实际开发中,经常遇到特殊需求,如日期格式转换、加密字段、枚举映射等,这时候就需要手动实现自定义逻辑。

场景一:自定义日期格式

在实际开发中,日期通常需要转为指定格式(如 YYYY-MM-DD HH:MM:SS),而 Rust 标准库的 SystemTime 或第三方库 chrono 的日期类型,默认序列化格式不符合需求,需手动实现序列化/反序列化。

首先,我们先添加上 chrono 依赖:

[dependencies]
chrono = { version = "0.4", features = ["serde"] }

示例代码如下所示:

use std::ops::Deref;
use chrono::{DateTime, Local, NaiveDateTime, TimeZone, offset::LocalResult};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};

// 自定义日期类型(封装 chrono::DateTime)
#[derive(Debug, Clone, Copy)]
struct CustomDateTime(DateTime<Local>);

// 实现 Serialize trait:转为 "YYYY-MM-DD HH:MM:SS" 格式
impl Serialize for CustomDateTime {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // self.0 访问内部的 DateTime<Local>
        let formatted = format!("{}", self.0.format("%Y-%m-%d %H:%M:%S"));
        serializer.serialize_str(&formatted)
    }
}

// 实现 Deserialize trait:从 "YYYY-MM-DD HH:MM:SS" 转为 CustomDateTime
impl<'de> Deserialize<'de> for CustomDateTime {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        // 解析为朴素时间 (NaiveDateTime)
        let naive =
            NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S").map_err(de::Error::custom)?;
        // 将朴素时间转换为本地时间 (Local)
        let dt_local = match Local.from_local_datetime(&naive) {
            LocalResult::Single(dt) => dt,
            _ => {
                return Err(de::Error::custom(
                    "Invalid or ambiguous local datetime due to DST transition",
                ));
            }
        };
        Ok(CustomDateTime(dt_local))
    }
}

// 实现 Deref 使得使用 DateTime<Local> 像一样使用 CustomDateTime
impl Deref for CustomDateTime {
    type Target = DateTime<Local>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// 使用自定义日期类型​
#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u32,
    name: String,
    register_time: CustomDateTime, // 使用自定义日期字段
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        id: 1002,
        name: "Bob".to_string(),
        register_time: CustomDateTime(Local::now()),
    };

    let json_str = serde_json::to_string(&user)?;
    println!("序列化结果: {}", json_str);

    // 反序列化
    let deserialized_user: User = serde_json::from_str(&json_str)?;
    println!("反序列化结果: {:?}", deserialized_user);

    Ok(())
}

执行 cargo run 可得到如下结果:

序列化结果: {"id":1002,"name":"Bob","register_time":"2026-04-06 14:05:27"}
反序列化结果: User { id: 1002, name: "Bob", register_time: CustomDateTime(2026-04-06T14:05:27+08:00) }

场景二:加密字段处理

在开发过程中,需要对于手机号、密码等敏感字段在序列化时进行加密,在反序列化时进行解密,这里我们通过 serialize_withdeserialize_with 来指定自定义函数。

use serde::{Deserialize, Deserializer, Serialize, Serializer};

// 模拟加密/解密函数(实际项目中替换为 AES、RSA 等真实加密算法)
fn encrypt(s: &str) -> String {
    s.chars().rev().collect() // 简单反转字符串模拟加密
}

fn decrypt(s: &str) -> String {
    s.chars().rev().collect() // 反转字符串模拟解密
}

// 自定义序列化函数(加密字段)
fn serialize_encrypted<S>(phone: &str, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let encrypted_phone = encrypt(phone);
    serializer.serialize_str(&encrypted_phone)
}

// 自定义反序列化函数(解密字段)
fn deserialize_encrypted<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: Deserializer<'de>,
{
    let encrypted_phone: String = Deserialize::deserialize(deserializer)?;
    let decrypted_phone = decrypt(&encrypted_phone);
    Ok(decrypted_phone)
}

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u32,
    name: String,
    // 指定自定义序列化/反序列化函数
    #[serde(
        serialize_with = "serialize_encrypted",
        deserialize_with = "deserialize_encrypted"
    )]
    phone: String, // 敏感字段,自动加密/解密
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        id: 1003,
        name: "Charlie".to_string(),
        phone: "13800138000".to_string(), // 原始未加密手机号
    };

    let json_str = serde_json::to_string(&user)?;
    println!("序列化结果: {}", json_str);

    // 反序列化
    let deserialized_user: User = serde_json::from_str(&json_str)?;
    println!("反序列化结果: {:?}", deserialized_user);

    Ok(())
}

执行 cargo run 可得到如下结果:

序列化结果: {"id":1003,"name":"Charlie","phone":"00083100831"}
反序列化结果: User { id: 1003, name: "Charlie", phone: "13800138000" }

场景三:枚举的自定义序列化

Rust 枚举默认序列化格式为枚举名称或索引,实际开发中常需要将枚举序列化为指定字符串或数字,这时候可以通过手动实现 trait 或使用 Serde 属性实现。

use serde::{Deserialize, Deserializer, Serialize, Serializer};

// 方式一:使用 Serde 属性(简单场景首选)
#[derive(Serialize, Deserialize, Debug)]
enum Status {
    #[serde(rename = "active")] // 序列化为 "active"
    Active,
    #[serde(rename = "inactive")] // 序列化为 "inactive"
    Inactive,
    #[serde(rename = "disabled")] // 序列化为 "disabled"
    Disabled,
}

// 方式二:手动实现 Serialize/Deserialize trait
#[derive(Debug)]
enum Role {
    Admin,
    User,
    Guest,
}

impl Serialize for Role {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // 将枚举序列化为对应的数字
        let role_num = match self {
            Role::Admin => 0,
            Role::User => 1,
            Role::Guest => 2,
        };
        serializer.serialize_u8(role_num)
    }
}

impl<'de> Deserialize<'de> for Role {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let role_num: u8 = Deserialize::deserialize(deserializer)?;
        // 从数字反序列化为枚举
        let role = match role_num {
            0 => Role::Admin,
            1 => Role::User,
            2 => Role::Guest,
            _ => return Err(serde::de::Error::custom("无效的角色值")),
        };
        Ok(role)
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u32,
    name: String,
    status: Status,
    role: Role,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        id: 1004,
        name: "David".to_string(),
        status: Status::Active,
        role: Role::Admin,
    };

    let json_str = serde_json::to_string(&user)?;
    println!("序列化结果: {}", json_str);

    // 反序列化
    let deserialized_user: User = serde_json::from_str(&json_str)?;
    println!("反序列化结果: {:?}", deserialized_user);

    Ok(())
}

执行 cargo run 可得到如下结果:

序列化结果: {"id":1004,"name":"David","status":"active","role":0}
反序列化结果: User { id: 1004, name: "David", status: Active, role: Admin }

场景四:跳过不必要的字段与自定义默认值

在开发过程中,部分字段(如内部备注、临时计算字段)不需要序列化,或者是反序列化时字段缺失需设置自定义默认值,可以通过 Serde 提供的属性快速实现。

use serde::Deserialize;

// 自定义默认值函数
fn default_page_size() -> u32 {
    10 // 分页默认大小为 10
}

#[derive(Deserialize, Debug)]
struct PageQuery {
    page: u32,
    #[serde(default = "default_page_size")] // 自定义默认值
    page_size: u32,
    #[serde(skip)] // 跳过该字段,不参与序列化/反序列化
    internal_note: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let json_str = r#"{"page": 1}"#;

    // 反序列化
    let deserialized_user: PageQuery = serde_json::from_str(&json_str)?;
    println!("反序列化结果: {:?}", deserialized_user);

    Ok(())
}

执行 cargo run 可得到如下结果:

反序列化结果: PageQuery { page: 1, page_size: 10, internal_note: "" }

总结

看完整篇文章,相信你也知道了 Serde 是兼顾性能与易用性的,四个进阶使用示例,光看示例代码就比较容易理解了,可以说,Serde 成为 Rust 生态中事实上的序列化标准库不是没有理由的,反而有那种“必然性”。