我正在将一堆文档反序列化为enum。我的文档有一个字段,可用于选择正确的变体。我希望实现以下三点:
- 如果标签匹配其中一个变体,则将其反序列化为此变体
- 如果标签不匹配,将其反序列化为
Other
变体。 - 如果标签匹配,但内容与定义的结构不匹配,则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")})
}