rust序列化和反序列化(json、yaml、toml)详解

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_jsonserde_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})
}

下一步行动

  1. 尝试在你的项目中使用 #[serde(flatten)] 优化嵌套结构体的JSON输出格式
  2. 为枚举类型添加 #[serde(tag = "type")] 属性,观察序列化后的JSON结构变化
  3. 如果需要处理特殊数据格式,尝试实现自定义的序列化/反序列化函数

空值的序列化与反序列化

Option 定义的字段,值为 None

  • 序列化:

    • toml:忽略字段,不序列化

    • json:序列化为null

    • yaml:序列化为null

  • 反序列化:

    • toml:无字段时,反序列化为 None

    • jsonnull 或 无字段时,反序列化为 None

    • yamlnull 或 无字段时,反序列化为 None

String类型的字段

  • 反序列化

    • toml:无字段时,反序列化错误

    • jsonnull 反序列化为字符串"null";无字段时,反序列化错误

    • yamlnull 反序列化为字符串"null";无字段时,反序列化错误

#[serde(default)] 标注字段默认值

  • 反序列化
    • jsonyaml:值为null时报错
    • 缺失相应字段时,反序列化为类型的默认值
      • 数值类型:0
      • 字符串类型:""
      • 布尔类型:false
      • HashMap:{}
      • Vec:[]
      • 结构体类型,需要对结构体实现 Default:结构体各字段反序列化为相应默认值
相关推荐
wjcroom2 小时前
融释涡旋理论-对狭义相对论和洛伦兹变换的兼容
开发语言·前端
大明者省2 小时前
Python 程序在 Ubuntu 系统的完整部署流程
开发语言·python·ubuntu
智算菩萨2 小时前
【Tkinter】14 事件处理机制深度解析:从基础绑定到高级传播,构建交互式绘图笔记应用
开发语言·笔记·python·microsoft·ui·ai编程·tkinter
東雪木2 小时前
Java学习——接口 (interface) 与抽象类 (abstract) 的本质区别、选型标准
java·开发语言·jvm·学习·java面试
小和尚敲木头2 小时前
router.push(‘/‘)跳转不触发重定向
开发语言·前端·javascript
_MyFavorite_2 小时前
JAVA重点基础、进阶知识及易错点总结(16)多线程基础(Thread & Runnable)
java·开发语言
misty youth2 小时前
提示词合集【自用】
开发语言·前端·ai编程
zero15972 小时前
Python 8天极速入门笔记(大模型工程师专用):第六篇-函数进阶 + 模块导入,大模型实战调用前置
开发语言·python·大模型编程语言
还是大剑师兰特2 小时前
为什么要用 import.meta.glob 加载 SVG 图标库
开发语言·前端·javascript