Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize "catch all" variant in a tagged enum

Tags:

enums

rust

serde

I am deserializing a bunch of documents into an enum. My documents have a field that can be used to pick the correct variant. I want the following three points to happen:

  1. if the tag matches one of the Variant, deserialize it into this variant
  2. if the tag doesn't match, deserialize it into the Other variant.
  3. if the tag matches but the content doesn't match the defined structure, panic.

I have tried the few following solutions but didn't manage to achieve all three points:

Just #[serde(other)]

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Document {
   Config {
      path: PathBuf,
   },
   #[serde(other)]
   Other,
}

With this, the content of the document is not deserialized if it doesn't match Config.

Untagged enum:

#[derive(Deserialize)]
#[serde(untagged)]
enum Document {
   Config {
      type: String,
      path: PathBuf,
   },
   Other(serde_yaml::Value),
}

With this one I get the content of documents that don't match as a Value. Unfortunately if somebody write a file of type: Config with a typo like say paht: /etc/, it will result in being deserialized as Other instead of panicking.

Finally, with a nested enum:

#[derive(Deserialize)]
#[serde(untagged)]
enum Document {
   Config(Config),
   Other(serde_yaml::Value),
}

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Config {
   path: PathBuf,
}

This seems to behave exactly like the previous case (simple untagged enum).

How can I get both Other deserialized as a serde_yaml::Value when nothing matches and panicking if the tag matches but not the content of the structure?

like image 371
ITChap Avatar asked Dec 22 '25 21:12

ITChap


1 Answers

This is (now? it isn't clear to me since when) implemented in serde. This is named #[serde(untagged)]:

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Document {
    Config {
        path: PathBuf,
    },
    #[serde(untagged)]
    Other {},
}

The {} is necessary, otherwise serde assumes there can't be properties in this object.

If you want to capture the data, you can do that too:

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Document {
    Config {
        path: PathBuf,
    },
    #[serde(untagged)]
    Other(serde_yaml::Value),
}
like image 91
Chayim Friedman Avatar answered Dec 24 '25 11:12

Chayim Friedman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!