rust序列化的高级玩法

187 阅读3分钟

今天带大家学习一些serde的小技巧,让你在工作中如鱼得水,应付各种序列化问题,比如我们把first_name使用#[serde(rename_all = "camelCase")]序列化成firstName,也可以跳过某些字段不被序列化,也可以丢弃数据,自定义格式显示日期。

将字母下划线命名字段序列化成驼峰命名规则

#[derive[Serialize]]
#[serde(rename_all = "cameCase")]
struct Users{
  user_name:String,
  user_age:i32,
  user_email:String
}

fn main(){
  let user = Users{
    user_name:"job".to_string(),
    user_age:12,
    user_email:"2859200@qq.com"
  }
  
  let josn = serde_json::to_string_pretty(&user).unwrap();
  println!("{}",json);
}

跳过序列化字段

⚠️使用 skip_serializing 不会跳过 反序列化字段。如果只添加 skip_serializing 属性,然后尝试反序列化数据,会失败,因为仍会尝试反序列化已跳过的字段。请使用 skip 属性来同时跳过序列化和反序列化(参见字段属性: skip)。同样,使用 skip_deserializing 来仅跳过反序列化。

use serde::Serialize;

use std::collections::BTreeMap as Map;

#[derive(Serialize)]
struct Resource {
    // 总是被序列化。
    name: String,

    // 从不被序列化。
    #[serde(skip_serializing)]
    hash: String,

    // 使用方法来决定是否跳过该字段。
    #[serde(skip_serializing_if = "Map::is_empty")]
    metadata: Map<StringString>,
}

fn main() {
    let resources = vec![
        Resource {
            name: "Stack Overflow".to_string(),
            hash: "b6469c3f31653d281bbbfa6f94d60fea130abe38".to_string(),
            metadata: Map::new(),
        },
        Resource {
            name: "GitHub".to_string(),
            hash: "5cb7a0c47e53854cd00e1a968de5abce1c124601".to_string(),
            metadata: {
                let mut metadata = Map::new();
                metadata.insert("headquarters".to_string(),
                                "San Francisco".to_string());
                metadata
            },
        },
    ];

    let json = serde_json::to_string_pretty(&resources).unwrap();

    // 打印:
    //
    //    [
    //      {
    //        "name": "Stack Overflow"
    //      },
    //      {
    //        "name": "GitHub",
    //        "metadata": {
    //          "headquarters": "San Francisco"
    //        }
    //      }
    //    ]
    println!("{}", json);
}

丢弃数据

use std::fmt;
use std::marker::PhantomData;

use serde::de::{
    self, Deserialize, DeserializeSeed, Deserializer, Visitor, SeqAccess,
    IgnoredAny,
};
use serde_json::json;

// 可用于仅反序列化序列中的第 `n` 个元素,并且可以高效地丢弃索引 `n` 之前或之后的任意类型元素的种子。
//
// 例如,仅反序列化索引为 3 的元素:
//
//    NthElement::new(3).deserialize(deserializer)
pub struct NthElement<T> {
    n: usize,
    marker: PhantomData<fn() -> T>,
}

impl<T> NthElement<T> {
    pub fn new(n: usize-> Self {
        NthElement {
            n: n,
            marker: PhantomData,
        }
    }
}

impl<'de, T> Visitor<'defor NthElement<T>
where
    T: Deserialize<'de>,
{
    type Value = T;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "我们关心索引 {} 处的序列"self.n)
    }

    fn visit_seq<V>(selfmut seq: V) -> Result<Self::Value, V::Error>
    where
        V: SeqAccess<'de>,
    {
        // 跳过前 `n` 个元素。
        for i in 0..self.n {
            // 如果在到达元素 `n` 之前序列结束,则出错。
            if seq.next_element::<IgnoredAny>()?.is_none() {
                return Err(de::Error::invalid_length(i, &self));
            }
        }

        // 反序列化我们关心的那个元素。
        let nth = seq.next_element()?
                     .ok_or_else(|| de::Error::invalid_length(self.n, &self))?;

        // 跳过 `n` 之后序列中的任何剩余元素。
        while let Some(IgnoredAny) = seq.next_element()? {
            // ignore
        }

        Ok(nth)
    }
}

impl<'de, T> DeserializeSeed<'defor NthElement<T>
where
    T: Deserialize<'de>,
{
    type Value = T;

    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(self)
    }
}

fn main() {
    let array = json!(["a""b""c""d""e"]);

    let nthString = NthElement::new(3).deserialize(&array).unwrap();

    println!("{}", nth);
    assert_eq!(nth, array[3]);
}

自定义格式显示日期

use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct StructWithCustomDate {
    // DateTime 可以直接支持 Serde,但使用 RFC3339 格式。提供一些自定义逻辑使其使用我们想要的格式。
    #[serde(with = "my_date_format")]
    pub timestamp: DateTime<Utc>,

    // 结构体中的其他字段。
    pub bidder: String,
}

mod my_date_format {
    use chrono::{DateTime, Utc, NaiveDateTime};
    use serde::{self, Deserialize, Serializer, Deserializer};

    const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

    // serialize_with 函数的签名必须遵循以下模式:
    //
    //    fn serialize<S>(&T, S) -> Result<S::Ok, S::Error>
    //    where
    //        S: Serializer
    //
    // 尽管也可以对输入类型 T 进行泛型化。
    pub fn serialize<S>(
        date: &DateTime<Utc>,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = format!("{}", date.format(FORMAT));
        serializer.serialize_str(&s)
    }

    // deserialize_with 函数的签名必须遵循以下模式:
    //
    //    fn deserialize<'de, D>(D) -> Result<T, D::Error>
    //    where
    //        D: Deserializer<'de>
    //
    // 尽管也可以对输出类型 T 进行泛型化。
    pub fn deserialize<'de, D>(
        deserializer: D,
    ) -> Result<DateTime<Utc>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let dt = NaiveDateTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)?;
        Ok(DateTime::<Utc>::from_naive_utc_and_offset(dt, Utc))
    }
}

fn main() {
    let json_str = r#"
      {
        "timestamp": "2017-02-16 21:54:30",
        "bidder": "Skrillex"
      }
    "#;

    let data: StructWithCustomDate = serde_json::from_str(json_str).unwrap();
    println!("{:#?}", data);

    let serialized = serde_json::to_string_pretty(&data).unwrap();
    println!("{}", serialized);
}

最后希望大家多多关注我的公众号:花说编程,此外加我微信更有rust学习资料赠送你