Skip to content

serde 介绍

serde 是用于通用数据的序列化和反序列 化的框架, 这里查看在线文档, 具体实现 还需要其他的包, 比如将 rust 结构体转换为 json 字符串, 或者将 json 转换 为 rust 结构体 就需要 serde_json

同理, yaml/toml/json5 等其他数据格式需要其对应的包

json

添加依赖

toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

序列化

  • Rust 结构体必须实现 serde::Serialize 这个 trait
  • 实现接口后就会提供这个方法: to_string

所谓的序列化 就是将 rust 中的结构体/枚举转换为指定格式的字符串 (如 json 格式)

rust
use serde::{Deserialize, Serialize};
use serde_json;

// 关键: 必须要实现则两个 trait 才能序列化
// 注意: 如果字段的值是 struct, 那它也必须实现这两个 trait
#[derive(Debug, Serialize, Deserialize)]
struct User {
    username: String,
    password: Option<String>, // 可选字段
    userinfo: UserInfo,
}

#[derive(Debug, Serialize, Deserialize)]
struct UserInfo {
    age: u8,
    height: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut user = User {
        username: String::from("alice"),
        password: Some(String::from("123456")),
        userinfo: UserInfo {
            age: 19,
            height: 190,
        },
    };

    // 1.将 Rust Struct 转换为字符串, 返回一个 Result
    let str = serde_json::to_string(&user)?;
    println!("{str}");
    // {"username":"alice","password":"123456","userinfo":{"age":19,"height":190}}

    // 2.可选字段,会被转为 null(JS 中的空值)
    user.password = None;
    let str2 = serde_json::to_string(&user)?;
    println!("{str2}");
    // {"username":"alice","password":null,"userinfo":{"age":19,"height":190}}

    // 3.转为带有缩进与换行的json格式, 这个方法是 json create 独有的
    // 不是来自于 serde::{Deserialize, Serialize}; 接口中的标准方法
    // 其他实现只有 to_string 和 from_str 两个方法
    let str3 = serde_json::to_string_pretty(&user)?;
    println!("{str3}");
    // {
    //   "username": "alice",
    //   "password": null,
    //   "userinfo": {
    //     "age": 19,
    //     "height": 190
    //   }
    // }

    Ok(())
}

反序列化

  • Rust 结构体必须实现 serde::DeSerialize 这个 trait
  • 实现接口后就会提供这个方法: from_str 这个 trait

所谓的 反序列化就是将字符串解析为rust中的结构体/枚举 以方便程序处理

rust
use serde::{Deserialize, Serialize};
use serde_json;

// 关键: 必须要实现则两个 trait 才能序列化
// 注意: 如果字段的值是 struct, 那它也必须实现这两个 trait
#[derive(Debug, Serialize, Deserialize)]
struct User {
    username: String,
    password: Option<String>, // 可选字段
    userinfo: UserInfo,
}

#[derive(Debug, Serialize, Deserialize)]
struct UserInfo {
    age: u8,
    height: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let str = r##"{
    "username": "alice",
    "password": "123456",
    "userinfo": {
        "age": 18,
        "height": 180
    }
}"##;

    // 原字符串
    println!("original json string:");
    println!("{str}");

    // 1.将 json 字符串解析为Rust Struct
    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");
    // User {
    //     username: "alice",
    //     password: Some("123456"),
    //     userinfo: UserInfo {
    //         age: 18,
    //         height: 180
    //     }
    // }


    // 2.可选字段, 会解析为 None
    let str = r##"{
    "username": "alice",
    "userinfo": {
        "age": 18,
        "height": 180
    }
}"##;
    println!("original json string:");
    println!("{str}");

    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");

    Ok(())
}

序列化:字段重命名

由于 Rust 推荐代码风格 struct 所有字段都是 snake_case, 但是序列化时候, 可能不是这样, 所以需要重命名

  • #[serde(rename_all = "camelCase")] 作用于整个结构体/枚举
取值结果示例值
"camelCase"userName
"PascalCase"UserName
"snake_case"user_name
"SCREAMING_SNAKE_CASE"USER_NAME
"kebab-case"user-name
"SCREAMING-KEBAB-CASE"USER-NAME
"lowercase"username
"UPPERCASE"USERNAME
rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct User {
    user_name: String,
    user_age: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        user_name: String::from("alice"),
        user_age: 11,
    };

    let str3 = serde_json::to_string_pretty(&user)?;
    println!("{str3}");
    // {
    //   "UserName": "alice",
    //   "UserAge": 11
    // }

    Ok(())
}
rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct User {
    user_name: String,

    // 作用于结构体/枚举具体的字段
    // rename 的优先级 rename_all 更高
    #[serde(rename = "kebab-case")]
    user_age: u8,

    // rename 可以手动指定字段名, 而不使用内置的那些值
    #[serde(rename = "user_height")]
    height: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        user_name: String::from("alice"),
        user_age: 11,
        height: 32,
    };

    let str3 = serde_json::to_string_pretty(&user)?;
    println!("{str3}");
    // {
    //   "UserName": "alice",
    //   "kebab-case": 11,
    //   "user_height": 32
    // }
    Ok(())
}

反序列化:字段默认值

rust
use serde::{Deserialize, Serialize};
use serde_json;

// 因为: rust 是强类型语言, 序列化之前, 它肯定有值, 否则无法编译
// 所以: 只需要测试反序列化时候的值就行
// 自定义序列化时, 字段的值

#[derive(Debug, Default, Serialize, Deserialize)]
// 1. 这样使用会调用 User::defalt(), 所以必须实现 Default trait
#[serde(default)]
struct User {
    // 2. struct 字段的值如果是 struct(String是一个struct),那么
    // 它也必须实现 Default 这个 trait
    user_name: String,
    user_age: u8, // 会调用 u8::default()
    user_height: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let str = r##"{}"##; // 空json对象字符串

    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");
    // 输出:
    // User {
    //     user_name: "",
    //     user_age: 0,
    //     user_height: 0
    // }

    Ok(())
}
rust
use serde::{Deserialize, Serialize};
use serde_json;

// 使用自定义的函数来设置默认值
fn default_user_age() -> u8 {
    1
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(default)]
struct User {
    user_name: String,
    #[serde(default = "default_user_age")]
    user_age: u8, // 会调用 default_user_age 而不是 u8::default()
    user_height: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let str = r##"{}"##; // 空json对象字符串

    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");
    // 输出:
    // User {
    //     user_name: "",
    //     user_age: 1,
    //     user_height: 0
    // }

    Ok(())
}

反序列化: 字段别名

rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Default, Serialize, Deserialize)]
struct User {
    // 别名
    #[serde(alias = "username")]
    user_name: String,

    // 可以设置多个 别名
    #[serde(alias = "age", alias = "userAge", alias = "UserAge")]
    user_age: u8,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let str = r##"{
    "username": "alice",
    "age": 11
}"##;

    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");
    // 输出:
    // User {
    //     user_name: "alice",
    //     user_age: 11
    // }

    let str = r##"{
    "username": "tom",
    "userAge": 12
}"##;
    let user = serde_json::from_str::<User>(str)?;
    println!("{user:?}");
    // User {
    //     user_name: "tom",
    //     user_age: 12
    // }

    Ok(())
}

统一设置枚举结构

rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Default, Serialize, Deserialize)]
enum UserInfo {
    Man(u8),   // 男:age
    Woman(u8), // 女:age

    // 将这个变体标记为默认值,需要配置 #[derive(Default)] 使用
    #[default]
    Unknown, // 保密
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct User {
    user_name: String,
    user_info: UserInfo,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        user_name: String::from("tom"),
        user_info: UserInfo::Man(12),
    };

    let str = serde_json::to_string_pretty(&user)?;
    println!("{str}");
    // {
    //   "user_name": "tom",
    //   "user_info": {
    //     "Man": 12
    //   }
    // }

    let user = User {
        user_name: String::from("tom"),
        user_info: UserInfo::Unknown,
    };
    let str = serde_json::to_string_pretty(&user)?;
    println!("{str}");
    // {
    //   "user_name": "tom",
    //   "user_info": "Unknown"
    // }

    // 在没有设置 tag 和 content 时: 序列化后的结构都不一样

    Ok(())
}
rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(tag = "gender", content = "age", rename_all = "UPPERCASE")]
enum UserInfo {
    Man(u8),   // 男:age
    Woman(u8), // 女:age

    // 将这个变体标记为默认值,需要配置 #[derive(Default)] 使用
    #[default]
    Unknown, // 保密
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct User {
    user_name: String,
    user_info: UserInfo,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        user_name: String::from("tom"),
        user_info: UserInfo::Man(12),
    };

    let str = serde_json::to_string_pretty(&user)?;
    println!("{str}");
    // {
    //   "user_name": "tom",
    //   "user_info": {
    //     "gender": "MAN",
    //     "age": 12
    //   }
    // }

    let user = User {
        user_name: String::from("tom"),
        user_info: UserInfo::Unknown,
    };
    let str = serde_json::to_string_pretty(&user)?;
    println!("{str}");
    // {
    //   "user_name": "tom",
    //   "user_info": {
    //     "gender": "UNKNOWN"
    //   }
    // }
    //
    // 统一序列化后的结构: user_info 必须是一个 object

    Ok(())
}

flatten 摊平结构字段

所谓的摊平结构的意思是:

txt
{
  "keyword": "Rust",
  "pagination": {
      "page": 1,
      "limit": 10
  }
}
摊平 pagination 字段:
{
  "keyword": "Rust",
  "page": 1,
  "limit": 10
}
rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Pagination {
    page: u32,
    limit: u8,
}

#[derive(Serialize, Deserialize, Debug)]
struct SearchQuery {
    keyword: String,

    #[serde(flatten)]
    pagination: Pagination,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let sq = SearchQuery {
        keyword: String::from("Rust"),
        pagination: Pagination { page: 1, limit: 10 },
    };

    let str = serde_json::to_string_pretty(&sq)?;
    println!("{str}");
    // {
    //   "keyword": "Rust",
    //   "page": 1,
    //   "limit": 10
    // }

    let str2 = r#"
    {
      "keyword": "Rust",
      "page": 1,
      "limit": 10
    }"#;
    let sq2 = serde_json::from_str::<SearchQuery>(&str2)?;
    println!("{sq2:?}");
    // SearchQuery {
    //     keyword: "Rust",
    //     pagination: Pagination {
    //         page: 1,
    //         limit: 10
    //     }
    // }

    // 注意: 摊平是双向的, 序列化和反序列化都同时生效
    let str3 = r#"
    {
      "keyword": "Rust",
      "pagination": {
          "page": 1,
          "limit": 10
      }
    }"#;

    // Error: Error("missing field `page`", line: 8, column: 5)
    let sq3 = serde_json::from_str::<SearchQuery>(&str3)?;
    println!("{sq3:?}");

    Ok(())
}

反序列化: 收集剩余字段

rust
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct SearchQuery {
    keyword: String,
    page: u32,
    limit: u8,

    #[serde(flatten)]
    extra: HashMap<String, serde_json::Value>, // 收集所有未知字段
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let str = r#"{
    "keyword": "Rust",
    "page": 1,
    "limit": 10,
    "count": 111,
    "sort_by": "id",
    "order_by": "desc"
}"#;

    let sq = serde_json::from_str::<SearchQuery>(&str)?;
    println!("{sq:?}");
    // SearchQuery {
    //     keyword: "Rust",
    //     page: 1,
    //     limit: 10,
    //     extra: {
    //         "count": Number(111),
    //         "sort_by": String("id"),
    //         "order_by": String("desc")
    //     }
    // }

    // 注: 此时 count 并不是一个标量类型, 而是一个结构体 serde::Number(111)
    let count_value = sq.extra.get("count").unwrap();

    // 将结构体转为 Rust 标量类型(通用转换方法)
    let count = serde_json::from_value::<i32>(count_value.clone())?;
    println!("{count:?}"); // i32

    // 使用内置快捷转换方法 as_str/as_u64/as_i64/as_array 这些方法是由
    // serde_json 提供的: https://docs.rs/serde_json/1.0.151/serde_json/enum.Value.html
    let count = count_value.as_u64().unwrap();
    println!("{count:?}"); // u64

    // 同理, serde::String() 也是需要转换的
    Ok(())
}

json5

注意 json 和 json5 格式的区别, 虽然都是字符串描述数据结构但是 json5 更加强大

rust
use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr;

use json5;

#[derive(Debug, Deserialize, Serialize)]
struct ServerConfig {
    workers: u64,
    domain: String,
    ip: Ipv4Addr,
    port: u16,
}

fn main() {
    // json5 string -> rust struct instance
    let server_config_json = r#"{
        // 服务器线程数量
        workers: 5,

        // 服务器域名
        domain: "www.example.com",

        // 服务器ip
        ip: "127.0.0.1",

        // 服务器监听的端口
        port: 6789
    }"#;

    let config: ServerConfig = json5::from_str(server_config_json).unwrap();
    println!("config");
    println!("{:?}", config);

    // rust struct instance -> json5 string
    let server_config = ServerConfig {
        workers: 10,
        domain: "localhost".to_string(),
        ip: Ipv4Addr::new(127, 0, 0, 1),
        port: 9876,
    };
    let config_str = json5::to_string(&server_config).unwrap();
    println!("config_str:\n{}", config_str);
}
toml
[dependencies]
json5 = "1.3"
serde = { version = "1.0", features = ["derive"] }

yaml

rust
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_yaml;

#[derive(Debug, Deserialize, Serialize)]
struct Container {
    image: String,
    restart: Option<String>,
    ports: Vec<String>,
    volumes: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize)]
struct DockerComposeConfig {
    services: HashMap<String, Container>,
    networks: Option<Vec<String>>,
    volumes: Option<Vec<String>>,
}

fn main() {
    // yaml string -> rust struct instance
    let docker_compose_yaml = r#"
    services:
        app:
            image: 'jc21/nginx-proxy-manager:latest'
            restart: unless-stopped
            ports:
                - '80:80'   # nginx http端口
                - '443:443' # nginx https端口
                - '81:81'   # nginx-proxy-manager 项目端口

            volumes:
                - ./data:/data
                - ./letsencrypt:/etc/letsencrypt
    "#;

    let config: DockerComposeConfig = serde_yaml::from_str(docker_compose_yaml).unwrap();
    println!("config \n {:?} \n", config);

    let yaml_str = serde_yaml::to_string(&config).unwrap();
    println!("yaml_str \n {:?} \n", yaml_str);
}
toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"

toml

rust
use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr;
use toml as serde_toml; // alias, keep same style with serde_json/serde_yaml

#[derive(Debug, Serialize, Deserialize)]
struct ServerAuth {
    require_password: bool,
    password: String,
    connection_timeout: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
    workers: u64,
    ip: Ipv4Addr,
    port: u16,
    domain: Option<String>,
    auth: Option<ServerAuth>,
}

fn main() {
    let toml_str = r#"
        # server config
        workers = 10
        ip = "192.168.2.1"
        port = 6789
        domain = "www.example.com"

        # auth config
        [auth]
        require_password = true
        password = "123456"
        connection_timeout = 30
    "#;

    let config: ServerConfig = serde_toml::from_str(toml_str).unwrap();

    println!("config:\n {:?} \n", config);

    let toml_str = serde_toml::to_string(&config).unwrap();
    println!("toml_str:\n{:?}\n", toml_str);
}
toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "1.1"

Released under the MIT License.