如何使用 serde 来 serialize/deserialize 具有包含数据的枚举键的 HashMap?

How to use serde to serialize/deserialize a HashMap with enum keys that contain data?

我需要序列化和反序列化一个 HashMap h,使用枚举 Foo 作为 JSON 的键。 Foo 的变体包含数据(这里简化为 u32,但实际上是枚举本身):

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

#[derive(Serialize, Deserialize)]
enum Foo {
  A(u32),
  B(u32),
}

// Tried several different things here! Just deriving the relevant traits doesn't work.
struct Bar {
  h: HashMap<Foo, i32>, // The i32 value type is arbitrary
}

fn main() {
  let mut bar = Bar { h: HashMap::new() };
  bar.h.insert(Foo::A(0), 1);

  // I want to be able to do this
  let bar_string = serde_json::to_string(&bar).unwrap();
  let bar_deser: Bar = serde_json::from_str(&bar_string).unwrap();
}

由于 JSON 规范要求键为字符串,我知道当它是 h 中的键时,我需要自定义为 Foo 完成序列化和反序列化的方式。我尝试了以下方法:

不幸的是,其中 none 个都起作用了——在编译成功后,所有这些最终都失败了,并出现了不同的恐慌。

如果有的话,在 serde 中实现我想要的东西的好方法是什么?如果没有,我将非常感谢任何解决方法的建议。

奖金问题:为什么 serde/serde_json 不提供默认序列化到 String + 像 Foo 这样的枚举的反序列化,当它用作 [=] 中的键时23=]?

这是 serde_with 帮助程序箱中 serde_as 的有效解决方案:

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

#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug)]
enum Foo {
    A(u32),
    B(u32),
}

#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Debug)]
struct Bar {
    #[serde_as(as = "HashMap<serde_with::json::JsonString, _>")]
    h: HashMap<Foo, i32>,
}

fn main() {
    let mut bar = Bar { h: HashMap::new() };
    bar.h.insert(Foo::A(0), 1);

    let bar_string = serde_json::to_string(&bar).unwrap();
    let bar_deser: Bar = serde_json::from_str(&bar_string).unwrap();

    println!("{:?}", bar);
    println!("{}", bar_string);
    println!("{:?}", bar_deser);
}
Bar { h: {A(0): 1} }
{"h":{"{\"A\":0}":1}}
Bar { h: {A(0): 1} }

Bonus question: why doesn't serde/serde_json provide a default serialization to a String + deserialization of an enum like Foo when it is used as a key in a HashMap?

如果您注意到,这些问题中的大部分都会变成运行时错误。

那是因为 serde 实际上 does have a representation of a map 适用于所有 Rust 值。所以serde本身对这个问题无能为力

serde_json 试图将地图输入 json Serializer 时,问题就来了。是的,这个序列化程序确实可以将任何键转换为 JSON 字符串,如果实现的话,还可以转换回来。

我认为这更像是一个设计决定,为什么不这样做。如果 JSON 只支持字符串键,那么 JSON 序列化器也应该支持。如果用户想使用其他类型作为键,他应该先将它们转换为字符串,如上面的代码示例所示。