我需要序列化和反序列化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规范要求键是字符串,我知道我需要自定义Foo
的序列化和反序列化方式,当它是h
中的一个键时。我尝试了以下方法:
- 序列化和反序列化的自定义实现(例如在这里接受的答案中)
- 服务器属性,例如:
#[serde(into = "String", try_from = "String")]
+实现Into<String> for Foo
和TryFrom<String> for Foo
(在这里的答案中描述) - 使用'serde_with' crate(也在这里的答案中描述)。
不幸的是,这些都没有成功-在编译成功后,所有这些最终都失败了。
在服务器中实现我想要的东西的好方法是什么,如果有的话?如果没有,我将非常感谢任何解决方法的建议。
附加问题:为什么serde/serde_json在HashMap
中用作键时,不为String
+反序列化的enum(如Foo
)提供默认序列化?
以下是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} }
附加问题:为什么serde/serde_json不提供默认序列化String +反序列化的enum,如Foo,当它被用作HashMap中的键时?
如果你注意到,这些问题大多变成了运行时错误。
这是因为serde
实际上确实具有与所有Rust值一起工作的映射的表示。所以serde
本身没有能力解决这个问题。
当serde_json
试图将映射馈送到jsonSerializer
时,问题就出现了。是的,如果实现了,这个序列化器确实可以将任何键转换为JSON字符串并返回。
我认为这更多的是一个设计决策,为什么没有这样做。如果JSON只支持字符串键,那么JSON序列化器也应该支持。如果用户想使用其他类型作为键,他应该首先将它们转换为字符串,如上面的代码示例所示。