Skip to content

创建项目安装依赖

sh
cargo new serde_demo
cd serde_demo
cargo add serde

serde 介绍

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

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

json

这里使用 json, 其他格式也是一样

rust
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Serialize, Deserialize, Debug)]
struct PkgInfo {
    cargo_url: String,
    repo_url: Option<String>,
    homepage: Option<String>,
}

// 1. 让 Package struct 实现 Serialize, Deserialize 特性
// Debug 是方便输出到控制台, 看是否转换成功
// PkgInfo 也必须要实现 Serialize, Deserialize 特性
#[derive(Serialize, Deserialize, Debug)]
struct Package {
    name: String,
    version: String,
    author: String,
    downloads: u64,
    pkg_info: PkgInfo,
    keywords: Vec<String>,
}

fn main() {
    // 2.json string => rust struct
    let mut json_file_path = String::new();
    json_file_path.push_str(env!("CARGO_MANIFEST_DIR"));
    json_file_path.push_str("/example.json");
    let json_str = fs::read_to_string(json_file_path).unwrap();

    let package: Package = serde_json::from_str(&json_str).unwrap();
    println!("{:?}", package);

    // 3.rust struct => json string
    let pack = Package {
        name: String::from("for-test-deserialize"),
        version: String::from("1.0.1"),
        author: String::from("secret"),
        downloads: 101,
        pkg_info: {
            PkgInfo {
                cargo_url: "http://localhost/cargo/for-test-deserialize".to_string(),
                repo_url: None,
                homepage: Some("http://localhost/for-test-deserialize".to_string()),
            }
        },
        keywords: Vec::from([
            "rust".to_string(),
            "json".to_string(),
            "serde_json".to_string(),
        ]),
    };


    let mut json_file_path = String::new();
    json_file_path.push_str(env!("CARGO_MANIFEST_DIR"));
    json_file_path.push_str("/example_2.json");

    // json_str -> example_2.json
    let json_str = serde_json::to_string(&pack).unwrap();
    fs::write(json_file_path, json_str).unwrap();

    println!("== finish ==");
}
toml
[dependencies]
serde = { version = "1.0.213", features = ["derive"] }
serde_json = "1.0.132"

json5

这个例子就不使用文件了, 总是要读取文件和处理路径, 还是直接使用 r#"xxx"# 原始字符串语法吧

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

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

#[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]
serde = { version = "1.0.213", features = ["derive"] }
json5 = "0.4.1"

yaml

rust
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

#[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.213", features = ["derive"] }
serde_yaml = "0.9.34"

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.213", features = ["derive"] }
toml = "0.8.19"

Released under the MIT License.