Serde JSON 反序列化枚举类型

3

I have an enum:

#[derive(Serialize, Deserialize)]
enum Action {
    Join,
    Leave,
}

还有一个结构体:

#[derive(Serialize, Deserialize)]
struct Message {
    action: Action,
}

然后我传递一个 JSON 字符串:

"{\"action\":0}" // `json_string` var

但是当我尝试像这样反序列化时:

let msg: Message = serde_json::from_str(json_string)?;

我遇到了错误,错误信息为:在第1行第11列处期望有值

如果我将JSON中的数字0替换为字符串"Join",它可以工作,但我希望该数字对应于Action枚举的值(0Action::Join1Action::Leave),因为它来自TypeScript请求。是否有简单的方法来实现这一点?


如果需要的话,您也可以添加一个匹配语句。 - Haseeb Saeed
2个回答

5

你想使用serde_repr

这里是该库README中的示例代码:

use serde_repr::{Serialize_repr, Deserialize_repr};

#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug)]
#[repr(u8)]
enum SmallPrime {
    Two = 2,
    Three = 3,
    Five = 5,
    Seven = 7,
}

fn main() -> serde_json::Result<()> {
    let j = serde_json::to_string(&SmallPrime::Seven)?;
    assert_eq!(j, "7");

    let p: SmallPrime = serde_json::from_str("2")?;
    assert_eq!(p, SmallPrime::Two);

    Ok(())
}

针对您的情况:

use serde_repr::{Serialize_repr, Deserialize_repr};

#[derive(Serialize_repr, Deserialize_repr)]
#[repr(u8)]
enum Action {
    Join = 0,
    Leave = 1,
}

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Message {
    action: Action,
}

2

在不添加任何额外依赖的情况下,最简洁的方法可能是使用“我最喜欢的serde技巧”——try_frominto容器属性。但在这种情况下,我认为自定义实现DeserializeSerialize更为合适:

impl<'de> Deserialize<'de> for Action {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        match i8::deserialize(deserializer)? {
            0 => Ok(Action::Join),
            1 => Ok(Action::Leave),
            _ => Err(serde::de::Error::custom("Expected 0 or 1 for action")),
        }
    }
}
impl Serialize for Action {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_i8(match self {
            Action::Join => 0,
            Action::Leave => 1,
        })
    }
}

自定义实现只重定向到序列化/反序列化。 Playground

太好了,这是一个很棒的选择,谢谢! - dudebro
反序列化会进入递归调用吗? - Voilin
@Voilin No: <Action as Deserialize>::deserialize 调用了 <i8 as Deserialize>::deserialize。因此,playground 终止了。(我不确定为什么我在序列化器和反序列化器之间没有保持一致性,我同样可以使用 0i8.serialize(serializer)(另一方面,deserializer.deserialize_i8() 将需要实现一个访问者。不行。)) - Caesar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接