反序列化"Catch All"标记枚举中的变体



我正在将一堆文档反序列化为enum。我的文档有一个字段,可用于选择正确的变体。我希望实现以下三点:

  1. 如果标签匹配其中一个变体,则将其反序列化为此变体
  2. 如果标签不匹配,将其反序列化为Other变体。
  3. 如果标签匹配,但内容与定义的结构不匹配,则panic。

我尝试了以下几种解决方案,但未能做到所有三点:

Just #[serde(other)]

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

这样,如果文档的内容与Config不匹配,则不会被反序列化。

未加标签的枚举:

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

使用这个,我得到不匹配为Value的文档的内容。不幸的是,如果有人写了一个type: Config的文件,其中有一个拼写错误,比如paht: /etc/,它将导致被反序列化为Other,而不是恐慌。

最后,使用嵌套enum:

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

这似乎与前一种情况(简单的无标记enum)完全相同。

我怎么能得到两个Other反序列化为serde_yaml::Value时,没有匹配和恐慌,如果标签匹配,但不是结构的内容?

另一种方法是将路径参数设为可选参数,以便在文件包含type:Config时使用Config enum变量:

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
enum Document {
    Config(Config),
    Other(serde_yaml::Value),
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
struct Config {
    path: Option<PathBuf>,
}
fn main() {
    const CONFIG_OK: &str = "type: Confignpath: /etc/passwd";
    let doc: Document = serde_yaml::from_str(CONFIG_OK).unwrap();
    println!("{doc:?}");
    //Config(Config { path: Some("/etc/passwd") })
    const CONFIG_BAD: &str = "type: Confignpaht: /etc/passwd";
    let doc: Document = serde_yaml::from_str(CONFIG_BAD).unwrap();
    println!("{doc:?}");
    //Config(Config { path: None })
    const OTHER: &str = "type: Othernkey: value";
    let doc: Document = serde_yaml::from_str(OTHER).unwrap();
    println!("{doc:?}");
    //Other(Mapping {"key": String("value")})
}

最新更新