Serde 是 Rust 生态中序列化/反序列化的事实标准。这篇记录常用的 serde 技巧,包括 derive 宏、字段控制、自定义序列化等。
基本用法
引入依赖:
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
最简单的用法,derive 一下就行:
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
name: String,
email: String,
}
fn main() {
let user = User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into(),
};
// 序列化
let json = serde_json::to_string_pretty(&user).unwrap();
println!("{}", json);
// 反序列化
let parsed: User = serde_json::from_str(&json).unwrap();
println!("{:?}", parsed);
}
输出:
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
字段重命名
API 经常用 snake_case,但 JSON 可能是 camelCase 或带特殊前缀。#[serde(rename)] 解决单个字段,#[serde(rename_all)] 批量处理:
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ApiResponse {
status_code: u16, // -> "statusCode"
error_message: String, // -> "errorMessage"
request_id: String, // -> "requestId"
}
#[derive(Serialize, Deserialize)]
struct Config {
#[serde(rename = "type")] // "type" 是 Rust 关键字
kind: String,
#[serde(rename = "max-retries")]
max_retries: u32,
}
rename_all 支持的命名风格:camelCase、PascalCase、snake_case、SCREAMING_SNAKE_CASE、kebab-case。
可选字段与默认值
#[derive(Serialize, Deserialize)]
struct Settings {
// JSON 中缺少此字段时用 None
#[serde(default)]
debug: bool,
// 可选字段,序列化时如果是 None 就不输出
#[serde(skip_serializing_if = "Option::is_none")]
proxy: Option<String>,
// 自定义默认值
#[serde(default = "default_timeout")]
timeout_ms: u64,
// 缺少时用 Vec 默认值(空数组)
#[serde(default)]
tags: Vec<String>,
}
fn default_timeout() -> u64 {
5000
}
这样就能处理 JSON 字段缺失的情况,不需要手动写 Option 判断。
常用 serde 属性一览
容器级别(#[serde(...)] 放在 struct/enum 上):
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] // 全局命名转换
#[serde(deny_unknown_fields)] // 遇到未知字段报错
#[serde(default)] // 所有字段使用 Default trait
struct Foo { /* ... */ }
字段级别:
#[derive(Serialize, Deserialize)]
struct Bar {
#[serde(rename = "type")] // 重命名
#[serde(alias = "kind")] // 反序列化时接受别名
#[serde(default)] // 缺失时用默认值
#[serde(skip)] // 完全跳过
#[serde(skip_serializing)] // 只跳过序列化
#[serde(skip_deserializing)] // 只跳过反序列化
#[serde(skip_serializing_if = "Option::is_none")] // 条件跳过
#[serde(flatten)] // 扁平化嵌套结构
#[serde(with = "module")] // 自定义序列化模块
field: String,
}
枚举序列化
Serde 对枚举的支持很灵活:
// 默认:外部标记 {"variant_name": data}
#[derive(Serialize, Deserialize)]
enum Message {
Text(String),
Image { url: String, width: u32 },
Quit,
}
// 内部标记:{"type": "Text", "content": "hello"}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Event {
Click { x: i32, y: i32 },
KeyPress { key: String },
Resize { width: u32, height: u32 },
}
// 相邻标记:{"t": "ok", "c": data}
#[derive(Serialize, Deserialize)]
#[serde(tag = "t", content = "c")]
enum ApiResult {
Ok(Data),
Err(String),
}
// 无标记:尝试按顺序匹配每个变体
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum StringOrNumber {
Str(String),
Num(f64),
}
自定义序列化
某些类型需要特殊的序列化逻辑,比如把时间戳序列化为指定格式的字符串:
mod datetime_format {
use chrono::NaiveDateTime;
use serde::{self, Deserialize, Serializer, Deserializer};
const FORMAT: &str = "%Y-%m-%d %H:%M:%S";
pub fn serialize<S>(date: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let s = date.format(FORMAT).to_string();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where D: Deserializer<'de> {
let s = String::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
}
#[derive(Serialize, Deserialize)]
struct Record {
id: u64,
#[serde(with = "datetime_format")]
created_at: NaiveDateTime,
}
用 #[serde(with = "module")] 指向自定义模块,模块需要提供 serialize 和 deserialize 两个函数。
Serde 的 derive 宏覆盖了绝大多数场景,很少需要手动实现 Serialize/Deserialize trait。属性系统设计得很完整,基本上想到的需求都有现成的属性可以用。