使用 serde_json 序列化具有非字符串键的映射 [英] Using serde_json to serialise maps with non-String keys

查看:50
本文介绍了使用 serde_json 序列化具有非字符串键的映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个测试用例:

use serde::{Serialize, Deserialize};
use std::collections::BTreeMap;
use std::fmt;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Incline {
    rise: u8,
    distance: u8,
}

impl Incline {
    pub fn new(rise: u8, distance: u8) -> Incline {
        Incline {rise, distance}
    }
}

impl fmt::Display for Incline {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.rise, self.distance)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn display_format() {
        let incline = Incline::new(4, 3);
        assert_eq!(format!("{}", incline), "4:3");
    }

    #[test]
    fn serialisation() {
        let key = Incline::new(4, 3);
        let value = "a steep hill";

        let mut map: BTreeMap<Incline, &str> = BTreeMap::new();
        map.insert(key, value);
        let serialised = serde_json::to_string(&map).unwrap();

        assert_eq!(serialised, r#"{"4:3":"a steep hill"}"#);
    }
}

display_format 测试按预期通过.

serialisation 测试失败并出现错误:

The serialisation test fails with an error:

thread 'tests::serialisation' panicked at 'called `Result::unwrap()` on an `Err` value: Error("key must be a string", line: 0, column: 0)', src/lib.rs:40:54

我如何告诉 serde_json 使用 Inclinestd::fmt::Display::fmt 实现来转动 Incline::new(4,3) 变成 "4:3"?

How do I tell serde_json to use Incline's implementation of std::fmt::Display::fmt to turn the Incline::new(4,3) into "4:3"?

推荐答案

通过更多的搜索,我意识到我必须自己实现序列化.

With a little more searching I realised that I had to implement serialise myself.

这样做:

use serde::{Serialize, Serializer};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Incline {
    rise: u8,
    distance: u8,
}

impl Incline {
    pub fn new(rise: u8, distance: u8) -> Incline {
        Incline {rise, distance}
    }
}

impl Serialize for Incline {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&format!("{}:{}", self.rise, self.distance))
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::*;

    #[test]
    fn serialisation() {
        let key = Incline::new(4, 3);
        let value = "a steep hill";

        let mut map: BTreeMap<Incline, &str> = BTreeMap::new();
        map.insert(key, value);
        let serialised = serde_json::to_string(&map).unwrap();

        assert_eq!(serialised, r#"{"4:3":"a steep hill"}"#);
    }
}

完整的序列化和反序列化如下所示:

In full, serialisation and deserialisation look like:

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{self, Visitor, Unexpected};
use std::fmt;
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Incline {
    rise: u8,
    distance: u8,
}

impl Incline {
    pub fn new(rise: u8, distance: u8) -> Incline {
        Incline {rise, distance}
    }
}

impl Serialize for Incline {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&format!("{}:{}", self.rise, self.distance))
    }
}

struct InclineVisitor;

impl<'de> Visitor<'de> for InclineVisitor {
    type Value = Incline;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a colon-separated pair of integers between 0 and 255")
    }

    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        let re = Regex::new(r"(\d+):(\d+)").unwrap(); // PERF: move this into a lazy_static!
        if let Some(nums) = re.captures_iter(s).next() {
            if let Ok(rise) = u8::from_str(&nums[1]) { // nums[0] is the whole match, so we must skip that
                if let Ok(distance) = u8::from_str(&nums[2]) {
                    Ok(Incline::new(rise, distance))
                } else {
                    Err(de::Error::invalid_value(Unexpected::Str(s), &self))
                }
            } else {
                Err(de::Error::invalid_value(Unexpected::Str(s), &self))
            }
        } else {
            Err(de::Error::invalid_value(Unexpected::Str(s), &self))
        }
    }

}

impl<'de> Deserialize<'de> for Incline {
    fn deserialize<D>(deserializer: D) -> Result<Incline, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_string(InclineVisitor)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::*;

    #[test]
    fn serialisation() {
        let key = Incline::new(4, 3);
        let value = "a steep hill";

        let mut map: BTreeMap<Incline, &str> = BTreeMap::new();
        map.insert(key, value);
        let serialised = serde_json::to_string(&map).unwrap();

        assert_eq!(serialised, r#"{"4:3":"a steep hill"}"#);
    }

    #[test]
    fn deserialisation() {
        let json = r#"{"4:3":"a steep hill"}"#;

        let deserialised: BTreeMap<Incline, &str> = serde_json::from_str(&json).unwrap();

        let key = Incline::new(4, 3);
        let value = "a steep hill";

        let mut map: BTreeMap<Incline, &str> = BTreeMap::new();
        map.insert(key, value);

        assert_eq!(deserialised, map);
    }
}

这篇关于使用 serde_json 序列化具有非字符串键的映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆