我如何支持 Serde 枚举的未知值或其他值?

Tra*_*kel 7 rust serde

我有一个 JSON API,它返回一个如下所示的对象:

{
  "PrivatePort": 2222,
  "PublicPort": 3333,
  "Type": "tcp"
}
Run Code Online (Sandbox Code Playgroud)

为了捕捉这一点,我有一个枚举和一个结构:

{
  "PrivatePort": 2222,
  "PublicPort": 3333,
  "Type": "tcp"
}
Run Code Online (Sandbox Code Playgroud)

目前,该 API 仅支持 中列出的三种协议PortType,但我们假设DCCP将来会添加对 的支持。我不希望 API 的客户端仅仅因为他们可能没有查看的配置选项中的未知字符串而开始失败。

为了解决这个问题,我添加了一个Unknown带有的变体String来表示值:

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PortMapping {
    pub private_port: u16,
    pub public_port: u16,
    #[serde(rename = "Type")]
    pub port_type: PortType,
}
Run Code Online (Sandbox Code Playgroud)

这里的目标是在PortType::Unknown("dccp")传入未知值时最终得到稍微不方便的值。当然,这不会做我想要的开箱即用的 - 传递未知"dccp"值将导致:

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    Unknown(String),
}
Run Code Online (Sandbox Code Playgroud)

是否有 Serde 配置来做我想做的事,还是我应该求助于手动编写DeserializeSerialize实现PortType

小智 12

尝试使用serde-enum-str

#[derive(serde_enum_str::Deserialize_enum_str, serde_enum_str::Serialize_enum_str)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    #[serde(other)]
    Unknown(String),
}
Run Code Online (Sandbox Code Playgroud)


Col*_*Two 7

这是一个问题,尽管它已经开放了 3 年,但到目前为止还没有完全解决。塞尔德#912

在这篇文章发表时,目前似乎正在实施的(尽管没有记录)是#[serde(other)]. 它只能应用于单位枚举字段,这限制了它的用处:

#[derive(Deserialize, PartialEq)]
#[serde(tag = "tag")]
enum Target {
   A(()),
   B(()),
   #[serde(other)]
   Others
}

fn main() {
    assert_eq!(Target::Others, from_str::<Target>(r#"{ "tag": "blablah" }"#).unwrap());
}
Run Code Online (Sandbox Code Playgroud)

除此之外,截至撰写本文时唯一的其他方法是编写您自己的Deserialize实现。


Rek*_*kby 6

我用 serde(from="String") 来做

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case", from="String")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    Unknown(String),
}

impl From<String> for PortType {
    fn from(s: String)->Self {
        use PortType::*;

        return match s.as_str() {
            "sctp" => Sctp,
            "tcp" => Tcp,
            "udp" => Udp,
            _ => Unknown(s)
        }
    }
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PortMapping {
    pub private_port: u16,
    pub public_port: u16,
    #[serde(rename = "Type")]
    pub port_type: PortType,
}
Run Code Online (Sandbox Code Playgroud)


Men*_*ndy 3

您现在可以#[serde(untagged)]在变体级别使用该属性来捕获未知变体。

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    #[serde(untagged)]
    Unknown(String),
}
Run Code Online (Sandbox Code Playgroud)

不要与枚举级别的未标记属性相混淆。

但我找不到此属性的任何文档。

谢谢https://github.com/serde-rs/serde/issues/912#issuecomment-1868785603