serde 是一个基于 Rust 的序列化库,可以把 Rust 的数据类型序列化为 serde 的数据模型,或是将字符串反序列化为 serde 的数据模型,再转化为 Rust 的数据类型。

Learn By Example

首先通过一个简单的 JSON (反)序列化例子来认识 serde:

Cargo.toml

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

main.rs

use serde::{Serialize, Deserialize};        // 导入自动实现 序列化/反序列化 的宏

#[derive(Debug, Serialize, Deserialize)]    // 自动实现序列化和反序列化
pub enum Gender {                           // 自定义的性别枚举结构体
    #[serde(alias = "female")]              // 为枚举添加别名
    Female,

    #[serde(alias = "male")]
    Male,
}

#[derive(Debug, Serialize, Deserialize)]
struct Person {                             // 自定义的结构体
    name: String,
    age: i32,
    gender: Gender
}

fn main() {
    let json = r#"{ "name": "Hoshino", "age": 4, "gender": "female" }"#;        // 被反序列化的 JSON 字符串
    let hoshino: Person = serde_json::from_str(json).unwrap();                  // 使用 serde_json 库进行 JSON 的反序列化

    println!("{:?}", hoshino);
}

大多数情况下,都可以用 derive 宏来自动实现结构体的序列化和反序列化,但是如果情况更复杂呢?比如要求性别不止有男性和女性:

pub enum Gender {
    Female,
    Male,
    Neither,
    Both,
    Trans { from: Gender, to: Gender }
}

这个时候,可以通过改良结构体的设计,但是这样可能会套很多层,不太方便,那么可以 自定义序列化实现

Custom Serialize

首先是自定义序列化:

#[derive(Debug)]
pub enum Gender {
    Female,                                         // 雌性
    Male,                                           // 雄性
    Neither,                                        // 无性
    Both,                                           // 中性
    Trans { from: Box<Gender>, to: Box<Gender> }    // 跨性别
}

impl Serialize for Gender {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
        S: Serializer {
        let str = match self {              // 根据枚举值来决定字符串内容
            Gender::Female => "female",
            Gender::Male => "male",
            Gender::Neither => "none",
            Gender::Both => "both",
            Gender::Trans { from, to } => {
                let pair = (from.as_ref(), to.as_ref());

                match pair {
                    (Gender::Female, Gender::Male) => "ftm",
                    (Gender::Male, Gender::Female) => "mtf",

                    _ => Err(<S as Serializer>::Error::custom("Trans variant only supports mtf and ftm"))?  // 为了简单这里只支持双性的跨性别
                }
            }
        };

        serializer.serialize_str(str)       // 序列化一个字符串
    }
}

#[derive(Debug, Serialize)]     // 注意删去 Deserialize,因为我们还没为 Gender 提供反序列化
struct Person {
    name: String,
    age: i32,
    gender: Gender
}

fn main() {
    let hoshino = Person {
        name: String::from("hoshino"),
        age: 4,
        gender: Gender::Trans { from: Gender::Male.into(), to: Gender::Female.into() }
    };

    let json = serde_json::to_string(&hoshino).unwrap();        // 使用 serde_json 库来进行 JSON 的序列化
    println!("{}", json);
}

输出:

{"name":"hoshino","age":4,"gender":"mtf"}

Custom Deserialize

自定义的反序列化有些麻烦,你需要先实现一个相应的 Visitor,再为 Deserializer 提供 Visitor 实现:

impl <'de> Deserialize<'de> for Gender {
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
        D: Deserializer<'de> {
        struct GenderVisitor;           // Gender 的 Visitor,用来反序列化

        impl <'de> Visitor<'de> for GenderVisitor {
            type Value = Gender;            // Visitor 的类型参数,这里我们需要反序列化的最终目标是 Gender

            fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {        // 必须重写的函数,用于为预期之外的类型提供错误信息
                formatter.write_str("expect 'female', 'male', 'none', 'both', 'mtf' and 'ftm'")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where      // 从字符串中反序列化
                E: DeError, {
                let result = match v {          // 普通的模式匹配
                    "female" => Gender::Female,
                    "male" => Gender::Male,
                    "none" => Gender::Neither,
                    "both" => Gender::Both,
                    "mtf" | "MtF" => Gender::Trans { from: Gender::Male.into(), to: Gender::Female.into() },
                    "ftm" | "FtM" => Gender::Trans { from: Gender::Female.into(), to: Gender::Male.into() },

                    _ => Err(E::custom(format!("unexpected: {}", v)))?  // 同上,为了简单起见不支持 mtf 和 ftm 之外的跨性别
                };

                Ok(result)
            }
        }

        deserializer.deserialize_str(GenderVisitor)         // 为 Deserializer 提供 Visitor
    }
}

fn main() {
    let json = r#"{ "name": "Hoshino", "age": 4, "gender": "mtf" }"#;
    let hoshino: Person = serde_json::from_str(json).unwrap();

    println!("{:?}", hoshino);
}

输出:

Person { name: "Hoshino", age: 4, gender: Trans { from: Male, to: Female } }

(De)Serializing When (De)Serializing

如果要把生理性别和心理性别分开来的话:

#[serde(rename_all = "lowercase")]              // 使用 rename_all 将所有枚举值重命名为小写
#[derive(Debug, Deserialize, Serialize)]        // 这里使用 derive 简化代码,Sex 的序列化不是本节要讨论的内容
pub enum Sex {
    Female,
    Male,
}

#[derive(Debug)]
pub enum Gender {
    Common(Sex),            // 常见的心理性别
    Neither,                // 无性
    Both,                   // 双性(或者中性)
    Trans(Sex)              // 跨性别
}

实现 Gender 的序列化:

impl Serialize for Gender {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
        S: Serializer {
        let str = match self {
            Gender::Common(sex) => return sex.serialize(serializer),        // 将序列化器交给 sex 进行序列化(注意:serialize 会吃掉所有权)
            Gender::Neither => "none",
            Gender::Both => "both",
            Gender::Trans(Sex::Female) => "ftm",
            Gender::Trans(Sex::Male) => "mtf",
        };

        serializer.serialize_str(str)
}

impl <'de> Deserialize<'de> for Gender {
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
        D: Deserializer<'de> {
        struct GenderVisitor;

        impl <'de> Visitor<'de> for GenderVisitor {
            type Value = Gender;

            fn expecting<'a>(&self, formatter: &mut Formatter<'a>) -> std::fmt::Result {
                formatter.write_str("expect female, male, none, both, mtf and ftm")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
                E: DeError, {
                // 这里需要显式标注,否则会出现编译错误
                let der: StringDeserializer<E> = v.to_string().into_deserializer();     // 将基础类型 str 转换为反序列化器
                let try_common = Sex::deserialize(der);     // 调用 Sex 的反序列化函数,返回 Result

                let result = match try_common {             // 对 Result 进行模式匹配
                    Ok(sex) => Gender::Common(sex),         // 反序列化成功,构造 Common
                    Err(_) => match v {                     // 反序列化失败,匹配其他值
                        "none" => Gender::Neither,
                        "both" => Gender::Both,
                        "mtf" => Gender::Trans(Sex::Male),
                        "ftm" => Gender::Trans(Sex::Female),

                        _ => Err(E::custom(format!("unexpected {}", v)))?,
                    }
                };

                Ok(result)
            }
        }

        deserializer.deserialize_str(GenderVisitor)
    }
}

最后

serde 不仅能(反)序列化为 JSON,还有 YAML,TOML 等多种语言,可以在 serde 的官方文档中找到它们。