跳到主要内容

serde 序列化框架

问题

serde 是如何工作的?为什么它是零成本的?

答案

serde 是 Rust 的序列化/反序列化框架,通过 derive 宏在编译时生成代码,运行时零开销。

基础用法

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
name: String,
age: u32,
#[serde(default)] // 缺失时使用默认值
email: Option<String>,
#[serde(rename = "created_at")] // 字段重命名
created: String,
#[serde(skip)] // 跳过此字段
cache: Vec<u8>,
}

fn main() -> serde_json::Result<()> {
// 序列化
let user = User {
name: "Alice".into(),
age: 30,
email: Some("alice@example.com".into()),
created: "2024-01-01".into(),
cache: vec![],
};
let json = serde_json::to_string_pretty(&user)?;

// 反序列化
let parsed: User = serde_json::from_str(&json)?;
println!("{:?}", parsed);

Ok(())
}

常用属性

属性位置作用
#[serde(rename = "...")]字段重命名字段
#[serde(default)]字段/结构体缺失时用默认值
#[serde(skip)]字段跳过此字段
#[serde(flatten)]字段扁平化嵌套结构
#[serde(rename_all = "camelCase")]结构体统一字段命名风格
#[serde(tag = "type")]枚举内部标签形式
#[serde(untagged)]枚举无标签形式

枚举序列化

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "data")] // 邻接标签
enum Message {
Text(String),
Image { url: String, width: u32 },
Ping,
}

// 序列化结果:
// {"type": "Text", "data": "hello"}
// {"type": "Image", "data": {"url": "...", "width": 100}}
// {"type": "Ping"}

支持的格式

serde 本身是格式无关的,通过不同 crate 支持各种格式:

格式crate用途
JSONserde_jsonAPI、配置
TOMLtomlCargo.toml 等配置
YAMLserde_yamlK8s 配置等
MessagePackrmp-serde高效二进制
Bincodebincode高性能二进制
RONronRust 风格配置

常见面试问题

Q1: serde 为什么高效?

答案

serde 通过 #[derive] 宏在编译时生成序列化代码,不使用反射。编译后的代码与手写的序列化逻辑一样快,且可被编译器内联优化。

Q2: #[serde(tag)] 的几种枚举表示有什么区别?

答案

标签模式属性JSON 输出
外部标签(默认){"Text": "hello"}
内部标签tag = "type"{"type": "Text", ...}
邻接标签tag = "t", content = "c"{"t": "Text", "c": "hello"}
无标签untagged"hello"

API 设计中最常用内部标签,与 TypeScript 的可辨识联合类似。

Q3: 如何自定义序列化逻辑?

答案

use serde::{Serializer, Deserializer};

#[derive(Serialize, Deserialize)]
struct Config {
#[serde(serialize_with = "serialize_duration")]
timeout: std::time::Duration,
}

fn serialize_duration<S: Serializer>(
dur: &std::time::Duration,
s: S,
) -> Result<S::Ok, S::Error> {
s.serialize_u64(dur.as_secs())
}

相关链接