虽然所有提供的答案都可以实现将您的HashMap
序列化为json,但它们是临时的或难以维护的。
允许特定数据结构作为映射中的键与serde
一起序列化的一种正确方法是与HashMap
中的整数键处理方式相同(有效):将值序列化为String
。这有几个优点,即
- 省略了中间数据结构,
- 无需克隆整个
HashMap
,
- 通过应用面向对象编程(OOP)概念更易于维护,以及
- 可在更复杂的结构(如
MultiMap
)中使用序列化。
编辑:创建serde_jdon_any_key是实现此目的最高效的方法。感谢@HighCommander4指出该创建。
或者,可以使用手动实现:
这可以通过手动实现您的数据类型的Serialize
和Deserialize
来完成。
我在地图上使用组合ID。
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Proj {
pub value: u64,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Doc {
pub proj: Proj,
pub value: u32,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Sec {
pub doc: Doc,
pub value: u32,
}
所以现在为它们手动实现serde
序列化有点麻烦,因此我们将实现委托给FromStr
和From<Self> for String
(Into<String>
概述)trait。
impl From<Doc> for String {
fn from(val: Doc) -> Self {
format!("{}{:08X}", val.proj, val.value)
}
}
impl FromStr for Doc {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match parse_doc(s) {
Ok((_, p)) => Ok(p),
Err(e) => Err(e.to_string()),
}
}
}
为了解析
Doc
,我们使用
nom
。下面的解析功能在他们的示例中有解释。
fn is_hex_digit(c: char) -> bool {
c.is_digit(16)
}
fn from_hex8(input: &str) -> Result<u32, std::num::ParseIntError> {
u32::from_str_radix(input, 16)
}
fn parse_hex8(input: &str) -> IResult<&str, u32> {
map_res(take_while_m_n(8, 8, is_hex_digit), from_hex8)(input)
}
fn parse_doc(input: &str) -> IResult<&str, Doc> {
let (input, proj) = parse_proj(input)?;
let (input, value) = parse_hex8(input)?;
Ok((input, Doc { value, proj }))
}
现在我们需要将
self.to_string()
和
str::parse(&str)
与
serde
连接起来,我们可以使用一个简单的宏来实现这一点。
macro_rules! serde_str {
($type:ty) => {
impl Serialize for $type {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s: String = self.clone().into();
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for $type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
paste! {deserializer.deserialize_string( [<$type Visitor>] {})}
}
}
paste! {struct [<$type Visitor>] {}}
impl<'de> Visitor<'de> for paste! {[<$type Visitor>]} {
type Value = $type;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("\"")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match str::parse(v) {
Ok(id) => Ok(id),
Err(_) => Err(serde::de::Error::custom("invalid format")),
}
}
}
};
}
这里我们使用 paste
来插值名称。请注意,现在结构体将始终按照上述定义进行序列化。永远不要作为结构体,总是作为字符串。
重要的是要实现 fn visit_str
而不是 fn visit_string
,因为 visit_string
推迟到 visit_str
。
最后,我们必须调用宏来自定义 struct
s。
serde_str!(Sec);
serde_str!(Doc);
serde_str!(Proj);
现在,指定的类型可以使用serde序列化为字符串并从字符串反序列化。