Search code examples
rustserde

How can I support an unknown or other value for a Serde enum?


I have a JSON API that returns an object that looks like this:

{
  "PrivatePort": 2222,
  "PublicPort": 3333,
  "Type": "tcp"
}

To capture this, I have an enum and a struct:

#[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,
}

Right now, this API only supports the three protocols listed in PortType, but let's assume that support for DCCP is added in the future. I do not want clients of the API to start failing simply because of an unknown string in a configuration option they might not be looking at.

To address this, I've added an Unknown variant with a String to represent the value:

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

The goal here is to end up with the slightly-inconvenient PortType::Unknown("dccp") value when an unknown value is passed in. Of course, this does not do what I would like out-of-box -- passing the unknown "dccp" value will result in:

Error("unknown variant `dccp`, expected one of `sctp`, `tcp`, `udp`, `unknown`", line: 1, column: 55)

Is there a Serde configuration for doing what I want or should I resort to manually writing Deserialize and Serialize implementations for PortType?


Solution

  • You can now use the #[serde(untagged)] attribute on the variant level, to capture unknown variants.

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

    Not to be confused with the untagged attribute on the enum level.

    I can't find any documentations for this attribute though.

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