Cargo.toml 添加依赖
toml
[dependencies]
# Serde核心库:启用derive宏支持自动序列化/反序列化
serde = { version = "1.0", features = ["derive"] }
# JSON格式:最通用的文本序列化格式
serde_json = "1.0"
# YAML格式:适合人类可读的配置文件(升级到稳定版)
serde_yaml = "0.9"
# TOML格式:Rust生态标准配置格式(使用标准版本号)
toml = "1.1" # 0.8.x 完全兼容TOML 1.1规范
# MessagePack格式:跨语言轻量级二进制格式
rmp-serde = "1.3"
| 术语 | 说明 |
|---|---|
| Serialize | 将 Rust 类型转换为字节/文本格式的 trait |
| Deserialize | 将字节/文本格式转换为 Rust 类型的 trait |
| Derive Macros | 通过 #[derive(Serialize, Deserialize)] 自动生成 trait 实现 |
| Data Format | 序列化目标格式(如 serde_json、serde_yaml) |
struct 序列化、 String 反序列化
serde_json::to_string_pretty生成带缩进的人类可读格式- 若追求最小文件体积,可使用
serde_json::to_string生成紧凑格式
rust
use serde::{Deserialize, Serialize};
#[derive(Serialize,Deserialize, Debug)]
struct TestConfig {
app_name: String,
version: String,
debug: bool,
}
fn main() {
let config = TestConfig {
app_name: "app".to_string(),
version: "1.0.0".to_string(),
debug: true,
};
// 结构体序列化为String
let json_string = serde_json::to_string_pretty(&config).unwrap();
let yaml_string=serde_yaml::to_string(&config).unwrap();
let toml_string=toml::to_string(&config).unwrap();
println!("JSON_序列化为字符串:{}", json_string);
println!("YAML_序列化为字符串:{}", yaml_string);
println!("TOML_序列化为字符串:{}", toml_string);
// String反序列化为结构体
let json_struct: TestConfig = serde_json::from_str(&json_string).unwrap();
let yaml_struct: TestConfig = serde_yaml::from_str(&yaml_string).unwrap();
let toml_struct: TestConfig = toml::from_str(&toml_string).unwrap();
println!("JSON_反序列化struct:{:?}", json_struct);
println!("YAML_反序列化struct:{:?}", yaml_struct);
println!("TOML_反序列化struct:{:?}", toml_struct);
}
输出:
bash
JSON_string:{
"app_name": "app",
"version": "1.0.0",
"debug": true
}
YAML_string:app_name: app
version: 1.0.0
debug: true
TOML_string:app_name = "app"
version = "1.0.0"
debug = true
JSON_struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
YAML_struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
TOML_struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
struct 序列化、vec[u8] 反序列化
rust
use serde::{Deserialize, Serialize};
#[derive(Serialize,Deserialize, Debug)]
struct TestConfig {
app_name: String,
version: String,
debug: bool,
}
fn main() {
let config = TestConfig {
app_name: "app".to_string(),
version: "1.0.0".to_string(),
debug: true,
};
// 结构体序列化为vec[u8]
let json_bytes = serde_json::to_vec(&config).unwrap();
let yaml_bytes = serde_yaml::to_string(&config).unwrap().as_bytes().to_vec();
let toml_bytes= toml::to_string(&config).unwrap().as_bytes().to_vec();
println!("JSON_序列化为vec字节:{:?}", json_bytes);
println!("YAML_序列化为vec字节:{:?}", yaml_bytes);
println!("TOML_序列化为vec字节:{:?}", toml_bytes);
// 反序列化vec[u8]为结构体
let json_struct: TestConfig = serde_json::from_slice(&json_bytes).unwrap();
let yaml_struct: TestConfig = serde_yaml::from_slice(&yaml_bytes).unwrap();
let toml_struct: TestConfig = toml::from_slice(&toml_bytes).unwrap();
println!("JSON_反序列化struct:{:?}", json_struct);
println!("YAML_反序列化struct:{:?}", yaml_struct);
println!("TOML_反序列化struct:{:?}", toml_struct);
}
输出:
bash
JSON_序列化为vec字节:[123, 34, 97, 112, 112, 95, 110, 97, 109, 101, 34, 58, 34, 97, 112, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 49, 46, 48, 46, 48, 34, 44, 34, 100, 101, 98, 117, 103, 34, 58, 116, 114, 117, 101, 125]
YAML_序列化为vec字节:[97, 112, 112, 95, 110, 97, 109, 101, 58, 32, 97, 112, 112, 10, 118, 101, 114, 115, 105, 111, 110, 58, 32, 49, 46, 48, 46, 48, 10, 100, 101, 98, 117, 103, 58, 32, 116, 114, 117, 101, 10]
TOML_序列化为vec字节:[97, 112, 112, 95, 110, 97, 109, 101, 32, 61, 32, 34, 97, 112, 112, 34, 10, 118, 101, 114, 115, 105, 111, 110, 32, 61, 32, 34, 49, 46, 48, 46, 48, 34, 10, 100, 101, 98, 117, 103, 32, 61, 32, 116, 114, 117, 101, 10]
JSON_反序列化struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
YAML_反序列化struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
TOML_反序列化struct:TestConfig { app_name: "app", version: "1.0.0", debug: true }
写配置文件
将配置写入到文件代码
rust
/// 写配置文件 <br>
/// 文件类型:json、yaml、toml <br>
/// (序列化的字符串 , 文件类型) -> config/config.文件类型
fn write_file(str: &str, ext: &str) -> Result<(), Box<dyn Error>> {
let dir = path::Path::new("config"); // 配置文件所在目录
fs::create_dir_all(dir)?; // 目录不存在时创建目录
let file_path = dir.join(format!("config.{}", ext)); // 配置文件路径
let mut file = OpenOptions::new()
.write(true) // 启用写入权限
.create(true) // 文件不存在则创建
.truncate(true) // 文件存在则清空内容(覆盖)
.open(file_path)?; // 打开文件
file.write_all(str.as_bytes())?; // 写配置文件
Ok(())
}
序列化后写入
rust
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::{fs, path};
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct TestConfig {
name: String,
age: u8,
nums: String,
is_in: bool,
map: HashMap<String, String>,
subjects: Vec<Subject>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Subject {
subject_name: String,
score: u8,
}
fn main() {
let mut config = TestConfig {
name: "李雷".to_string(),
age: 17,
nums: "117".to_string(),
is_in: true,
map: HashMap::new(),
subjects: vec![
Subject {
subject_name: "语文".to_string(),
score: 97,
},
Subject {
subject_name: "数学".to_string(),
score: 93,
},
],
};
config.map.insert("key1".to_string(), "val1".to_string());
config.map.insert("key2".to_string(), "val2".to_string());
// 序列化为字符串
let json_string = serde_json::to_string_pretty(&config).unwrap();
let yaml_string = serde_yaml::to_string(&config).unwrap();
let toml_string = toml::to_string(&config).unwrap();
// 字符串写入到配置文件
if let Ok(()) = write_file(&json_string, &"json") {
println!("json保存成功");
}
if let Ok(()) = write_file(&yaml_string, &"yaml") {
println!("yaml保存成功");
}
if let Ok(()) = write_file(&toml_string, &"toml") {
println!("toml保存成功");
}
}
写完成后的 config/config.json
json
{
"name": "李雷",
"age": 17,
"nums": "117",
"isIn": true,
"map": {
"key2": "val2",
"key1": "val1"
},
"subjects": [
{
"subjectName": "语文",
"score": 97
},
{
"subjectName": "数学",
"score": 93
}
]
}
写完成后的 config/config.yaml
yaml
name: 李雷
age: 17
nums: '117'
isIn: true
map:
key2: val2
key1: val1
subjects:
- subjectName: 语文
score: 97
- subjectName: 数学
score: 93
写完成后的 config/config.toml
toml
name = "李雷"
age = 17
nums = "117"
isIn = true
[map]
key2 = "val2"
key1 = "val1"
[[subjects]]
subjectName = "语文"
score = 97
[[subjects]]
subjectName = "数学"
score = 93
读配置文件
将配置文件读取为 String
rust
/// 读配置文件 <br>
/// (文件类型 ) -> String
fn read_file(ext: &str) -> Result<String, Box<dyn Error>> {
let dir = path::Path::new("config"); // 配置文件所在目录
let file_path = dir.join(format!("config.{}", ext)); // 配置文件路径
if !file_path.exists() { // 配置文件不存在,直接返回错误
return Err("配置文件按不存在".into());
}
let mut buf = String::new(); // 存储读取到的配置信息
let mut file = File::open(file_path)?; // 打开配置文件
file.read_to_string(&mut buf)?; // 读取配置信息到 buf 中
Ok(buf) // 返回读取到的信息
}
反序列化读取到的配置文件
rust
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::{fs, path};
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct TestConfig {
name: String,
age: u8,
nums: String,
is_in: bool,
map: HashMap<String, String>,
subjects: Vec<Subject>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Subject {
subject_name: String,
score: u8,
}
fn main() {
// 读配置文件
if let Ok(str) = read_file("json") {
let json_s = serde_json::from_str::<TestConfig>(&str).unwrap();
println!("json读取文件反序列化:{:?}", json_s);
}
if let Ok(str) = read_file("yaml") {
let yaml_s = serde_yaml::from_str::<TestConfig>(&str).unwrap();
println!("yaml读取文件反序列化:{:?}", yaml_s);
}
if let Ok(str) = read_file("toml") {
let toml_s = toml::from_str::<TestConfig>(&str).unwrap();
println!("toml读取文件反序列化:{:?}", toml_s);
}
}
输出:
bash
json读取文件反序列化:TestConfig { name: "李雷", age: 17, nums: "117", is_in: true, map: {"key1": "val1", "key2": "val2"}, subjects: [Subject { subject_name: "语文", score: 97 }, Subject { subject_name: "数学", score: 93 }] }
yaml读取文件反序列化:TestConfig { name: "李雷", age: 17, nums: "117", is_in: true, map: {"key1": "val1", "key2": "val2"}, subjects: [Subject { subject_name: "语文", score: 97 }, Subject { subject_name: "数学", score: 93 }] }
toml读取文件反序列化:TestConfig { name: "李雷", age: 17, nums: "117", is_in: true, map: {"key1": "val1", "key2": "val2"}, subjects: [Subject { subject_name: "语文", score: 97 }, Subject { subject_name: "数学", score: 93 }] }
Serde 常用属性修饰全解
Serde 是 Rust 生态中最强大的序列化/反序列化框架,通过丰富的属性修饰可以灵活控制数据转换行为。以下是分类整理的常用属性,每个属性都包含作用说明 、代码示例 和关键注释:
一、字段重命名与映射
1. 单字段重命名 #[serde(rename = "new_name")]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct User {
#[serde(rename = "userId")] // 将Rust字段`id`重命名为JSON中的`userId`
id: u32,
name: String,
}
fn main() {
let user = User { id: 1, name: "Alice".into() };
let json = serde_json::to_string_pretty(&user).unwrap();
println!("{}", json);
// 输出: {"userId":1,"name":"Alice"}
}
2. 批量重命名 #[serde(rename_all = "case_style")]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] // 批量将所有snake_case字段转为camelCase
struct Config {
database_url: String, // 会被序列化为`databaseUrl`
max_connections: u32, // 会被序列化为`maxConnections`
enable_debug: bool, // 会被序列化为`enableDebug`
}
fn main() {
let config = Config {
database_url: "postgres://localhost/db".into(),
max_connections: 100,
enable_debug: true,
};
let json = serde_json::to_string_pretty(&config).unwrap();
println!("{}", json);
}
支持的命名风格:
| 风格值 | 示例 |
|---|---|
snake_case |
max_connections |
camelCase |
maxConnections |
PascalCase |
MaxConnections |
SCREAMING_SNAKE_CASE |
MAX_CONNECTIONS |
kebab-case |
max-connections |
二、字段包含与排除
1. 永久跳过字段 #[serde(skip)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
#[serde(skip)] // 序列化/反序列化均忽略该字段
password_hash: String, // 敏感信息永远不会被暴露
}
fn main() {
let user = User {
id: 1,
name: "Alice".into(),
password_hash: "hashed_password_123".into(),
};
let json = serde_json::to_string_pretty(&user).unwrap();
println!("{}", json);
// 输出: {"id":1,"name":"Alice"} (password_hash被完全忽略)
}
2. 条件跳过序列化 #[serde(skip_serializing_if = "condition")]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct Post {
title: String,
#[serde(skip_serializing_if = "Option::is_none")] // 空值时跳过序列化
excerpt: Option<String>, // 可选字段,为空时不显示在JSON中
content: String,
}
fn main() {
let post = Post {
title: "Rust Serde Guide".into(),
excerpt: None, // 空值,不会被序列化
content: "Serde is awesome!".into(),
};
let json = serde_json::to_string_pretty(&post).unwrap();
println!("{}", json);
// 输出: {"title":"Rust Serde Guide","content":"Serde is awesome!"}
}
3. 扁平化嵌套结构体 #[serde(flatten)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct Profile {
email: String,
age: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
#[serde(flatten)] // 将Profile的字段直接合并到User的JSON中
profile: Profile,
}
fn main() {
let user = User {
id: 1,
name: "Alice".into(),
profile: Profile {
email: "alice@example.com".into(),
age: 30,
},
};
let json = serde_json::to_string_pretty(&user).unwrap();
println!("{}", json);
// 输出: {"id":1,"name":"Alice","email":"alice@example.com","age":30}
}
三、默认值与缺失字段处理
1. 缺失字段使用默认值 #[serde(default)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct Config {
database_url: String,
#[serde(default)] // 反序列化时缺失则使用类型默认值
port: u16, // 默认值为0
#[serde(default = "default_timeout")] // 使用自定义默认值函数
timeout_seconds: u64,
}
// 自定义默认值函数
fn default_timeout() -> u64 {
30 // 默认超时30秒
}
fn main() {
// 反序列化时缺失port和timeout_seconds字段
let json_str = r#"{"database_url": "postgres://localhost/db"}"#;
let config: Config = serde_json::from_str(json_str).unwrap();
println!("{:?}", config);
// 输出: Config { database_url: "postgres://localhost/db", port: 0, timeout_seconds: 30 }
}
2. 拒绝未知字段 #[serde(deny_unknown_fields)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] // 反序列化时遇到未知字段直接报错
struct User {
id: u32,
name: String,
}
fn main() {
// JSON中包含未知字段`age`
let json_str = r#"{"id":1,"name":"Alice","age":30}"#;
let result = serde_json::from_str::<User>(json_str);
println!("{:?}", result);
// 输出: Err(Error("unknown field `age`", line: 1, column: 36))
}
四、枚举专用属性
1. 添加类型标签 #[serde(tag = "type")]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "message_type")] // 添加类型标签字段
enum Message {
Text(String),
Image { url: String, size: u64 },
Ping,
}
fn main() {
let text_msg = Message::Text("Hello World!".into());
let json = serde_json::to_string_pretty(&text_msg).unwrap();
println!("{}", json);
// 输出: {"message_type":"Text","value":"Hello World!"}
let image_msg = Message::Image {
url: "https://example.com/image.jpg".into(),
size: 1024,
};
let json = serde_json::to_string_pretty(&image_msg).unwrap();
println!("{}", json);
// 输出: {"message_type":"Image","url":"https://example.com/image.jpg","size":1024}
}
2. 无标签枚举 #[serde(untagged)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)] // 无标签,自动根据值类型推断枚举变体
enum Value {
Number(u64),
Text(String),
Boolean(bool),
}
fn main() {
let num: Value = serde_json::from_str("123").unwrap();
println!("{:?}", num); // 输出: Number(123)
let text: Value = serde_json::from_str(r#""hello""#).unwrap();
println!("{:?}", text); // 输出: Text("hello")
let bool: Value = serde_json::from_str("true").unwrap();
println!("{:?}", bool); // 输出: Boolean(true)
}
五、高级自定义
1. 自定义序列化函数 #[serde(serialize_with = "fn_name")]
rust
use serde::{Serialize, Serializer};
use serde_json::json;
use chrono::{Utc, DateTime};
// 自定义序列化函数:将时间戳转为RFC3339格式字符串
fn serialize_timestamp<S>(dt: &DateTime<Utc>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&dt.to_rfc3339())
}
#[derive(Debug, Serialize)]
struct Event {
name: String,
#[serde(serialize_with = "serialize_timestamp")] // 使用自定义序列化函数
timestamp: DateTime<Utc>,
}
fn main() {
let event = Event {
name: "user_login".into(),
timestamp: Utc::now(),
};
let json = serde_json::to_string_pretty(&event).unwrap();
println!("{}", json);
// 输出: {"name":"user_login","timestamp":"2024-05-20T12:34:56Z"}
}
2. 透明结构体 #[serde(transparent)]
rust
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)] // 将结构体透明化为内部字段的类型
struct UserId(u32); // 仅适用于单字段结构体
fn main() {
let user_id = UserId(123);
let json = serde_json::to_string_pretty(&user_id).unwrap();
println!("{}", json);
// 输出: 123 (而非 {"UserId":123})
}
下一步行动
- 尝试在你的项目中使用
#[serde(flatten)]优化嵌套结构体的JSON输出格式 - 为枚举类型添加
#[serde(tag = "type")]属性,观察序列化后的JSON结构变化 - 如果需要处理特殊数据格式,尝试实现自定义的序列化/反序列化函数
空值的序列化与反序列化
Option 定义的字段,值为 None
-
序列化:
-
toml:忽略字段,不序列化 -
json:序列化为null -
yaml:序列化为null
-
-
反序列化:
-
toml:无字段时,反序列化为None -
json:null或 无字段时,反序列化为None -
yaml:null或 无字段时,反序列化为None
-
String类型的字段
-
反序列化
-
toml:无字段时,反序列化错误 -
json:null反序列化为字符串"null";无字段时,反序列化错误 -
yaml:null反序列化为字符串"null";无字段时,反序列化错误
-
#[serde(default)] 标注字段默认值
- 反序列化
json、yaml:值为null时报错- 缺失相应字段时,反序列化为类型的默认值
- 数值类型:0
- 字符串类型:""
- 布尔类型:false
- HashMap:{}
- Vec:[]
- 结构体类型,需要对结构体实现
Default:结构体各字段反序列化为相应默认值