rust 反序列化标记枚举中的“Catch All”变量

bpzcxfmw  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(111)

我正在将一堆文档反序列化到一个枚举中。我的文档有一个字段可以用来选择正确的变量。我希望以下三点发生:
1.如果标记与某个变量匹配,则将其反序列化为此变量
1.如果标记不匹配,则将其反序列化为Other变量。
1.如果标记匹配,但内容与定义的结构不匹配,则会出现异常。
我已经尝试了以下几种解决方案,但未能实现所有三点:
#[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,而不是恐慌。
最后,使用嵌套枚举:


# [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,并在标记匹配但结构内容不匹配的情况下使其混乱?

jq6vz3qz

jq6vz3qz1#

另一种方法是将path参数设置为可选参数,以便每当文件包含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")})
}

相关问题