在标记枚举中反序列化“Catch All”变体

3
我正在将一堆文档反序列化为枚举类型。我的文档有一个字段,可以用来选择正确的变体。我希望实现以下三个点:
  1. 如果标签与 Variant 中的某个匹配,则将其反序列化为此变体
  2. 如果标签不匹配,则将其反序列化为 Other 变体。
  3. 如果标签匹配但内容与定义的结构不匹配,则出现 panic。
我尝试了以下几种解决方案,但没有实现以上所有三个点:
只使用 #[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/,那么它将被反序列化为其他而不是抛出异常。

最后,使用嵌套枚举:

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

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

这似乎与之前的情况(简单的未标记枚举)完全相同。

当没有匹配项时,如何将Other反序列化为serde_yaml :: Value ,如果标记匹配但结构内容不符,则会导致恐慌?

1个回答

0

另一种方法是将路径参数设为可选,这样当文件包含type: Config时,就会使用Config枚举变量:

#[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: Config\npath: /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: Config\npaht: /etc/passwd";
    let doc: Document = serde_yaml::from_str(CONFIG_BAD).unwrap();
    println!("{doc:?}");
    //Config(Config { path: None })

    const OTHER: &str = "type: Other\nkey: value";
    let doc: Document = serde_yaml::from_str(OTHER).unwrap();
    println!("{doc:?}");
    //Other(Mapping {"key": String("value")})
}


我认为这不会起作用。在文档上添加标签意味着您需要在“其他”上添加serde(other),这将需要一个单元类型并且无法反序列化。 - ITChap

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