如何使用Serde序列化HashMap,并将结构作为JSON的键?

时间:2018-07-11 03:40:17

标签: rust serde serde-json

我想使用结构作为键序列化一个HashMap

extern crate serde_json; // 1.0.22
#[macro_use]
extern crate serde_derive; // 1.0.68

use std::collections::HashMap;

fn main() {
    #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
    struct Foo {
        x: u64,
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct Bar {
        x: HashMap<Foo, f64>,
    }

    let mut p = Bar { x: HashMap::new() };
    p.x.insert(Foo { x: 0 }, 0.0);
    let serialized = serde_json::to_string(&p).unwrap();
}

该代码可以编译,但是当我运行它时,我得到一个错误:

Error("key must be a string", line: 0, column: 0)'

我更改了代码:

#[derive(Serialize, Deserialize, Debug)]
struct Bar {
    x: HashMap<u64, f64>,
}

let mut p = Bar { x: HashMap::new() };
p.x.insert(0, 0.0);
let serialized = serde_json::to_string(&p).unwrap();

HashMap中的键现在是u64,而不是字符串。为什么第一个代码给出错误?

3 个答案:

答案 0 :(得分:1)

为了将自定义数据类型序列化为JSON(或任何其他格式),您需要实现Serialize特征。否则Serde不知道如何序列化您的类型。

应该遵循以下原则:

impl Display for Foo {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(f, "{}", self.x)
    }
}

impl Serialize for Bar {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(self.x.len()))?;
        for (k, v) in &self.x {
            map.serialize_entry(&k.to_string(), &v)?;
        }
        map.end()
    }
}

playground

如果您的数据结构是枚举,那么您将需要更改Foo的{​​{1}}实现。

答案 1 :(得分:1)

您可以使用serde_with crate中的serde_as来将HashMap编码为键值对序列:

use serde_with::serde_as; // 1.5.1

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
struct Bar {
    #[serde_as(as = "Vec<(_, _)>"]
    x: HashMap<Foo, f64>,
}

哪个将序列化为此(并反序列化):

{
  "x":[
    [{"x": 0}, 0.0],
    [{"x": 1}, 0.0],
    [{"x": 2}, 0.0]
  ]
}

HashMap转换为Vec可能会产生一些开销,但这很方便。

答案 2 :(得分:0)

我找到了防弹解决方案?

  • 不需要额外的依赖
  • 兼容 HashMapBTreeMap 和其他可迭代类型
  • flexbuffers 一起使用

以下代码将字段(地图)转换为中间 Vec 表示:

pub mod vectorize {
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use std::iter::FromIterator;

    pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
        T: IntoIterator<Item = (&'a K, &'a V)>,
        K: Serialize + 'a,
        V: Serialize + 'a,
    {
        let container: Vec<_> = target.into_iter().collect();
        serde::Serialize::serialize(&container, ser)
    }

    pub fn deserialize<'de, T, K, V, D>(des: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
        T: FromIterator<(K, V)>,
        K: Deserialize<'de>,
        V: Deserialize<'de>,
    {
        let container: Vec<_> = serde::Deserialize::deserialize(des)?;
        Ok(T::from_iter(container.into_iter()))
    }
}

要使用它,只需添加模块的名称作为属性:

#[derive(Debug, Serialize, Deserialize)]
struct MyComplexType {
    #[serde(with = "vectorize")]
    map: HashMap<MyKey, String>,
}

如果你想在本地查看,剩下的部分:

use anyhow::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct MyKey {
    one: String,
    two: u16,
    more: Vec<u8>,
}

#[derive(Debug, Serialize, Deserialize)]
struct MyComplexType {
    #[serde(with = "vectorize")]
    map: HashMap<MyKey, String>,
}

fn main() -> Result<(), Error> {
    let key = MyKey {
        one: "1".into(),
        two: 2,
        more: vec![1, 2, 3],
    };
    let mut map = HashMap::new();
    map.insert(key.clone(), "value".into());
    let instance = MyComplexType { map };
    let serialized = serde_json::to_string(&instance)?;
    println!("JSON: {}", serialized);
    let deserialized: MyComplexType = serde_json::from_str(&serialized)?;
    let expected_value = "value".to_string();
    assert_eq!(deserialized.map.get(&key), Some(&expected_value));
    Ok(())
}

在 Rust 游乐场:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bf1773b6e501a0ea255ccdf8ce37e74d