在Rust中无需自定义反序列化器反序列化含单元/非单元的枚举
解决方案
要让Animal::Cat变体同时兼容JSON字符串"Cat"(对应Cat(None))和JSON对象{"Cat": "cat_name"}(对应Cat(Some("cat_name"))),可以通过定义辅助枚举+轻量反序列化辅助函数实现,无需编写整个枚举的自定义反序列化器,具体代码如下:
use serde::{Deserialize, Deserializer, Serialize}; // 辅助枚举:兼容两种JSON格式的Cat值 #[derive(Deserialize)] #[serde(untagged)] enum CatValue { Unit, Name(String), } // 将辅助枚举转换为业务需要的Option<String> fn cat_from_value<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> where D: Deserializer<'de>, { let value = CatValue::deserialize(deserializer)?; Ok(match value { CatValue::Unit => None, CatValue::Name(name) => Some(name), }) } #[derive(Serialize, Deserialize, Debug)] enum Animal { // 为Cat变体指定自定义反序列化逻辑 #[serde(deserialize_with = "cat_from_value")] Cat(Option<String>), Dog(String), Bird, } fn main() { let json_animals = r#" [ {"Cat": "Meow"}, "Cat", {"Dog": "Bark"}, "Bird" ]"#; println!("{:?}", serde_json::from_str::<Vec<Animal>>(json_animals).unwrap()); }
关键说明
CatValue是无标签枚举,会自动匹配两种JSON格式:遇到字符串"Cat"时解析为CatValue::Unit,遇到对象{"Cat": "..."}时解析为CatValue::Name(...)。cat_from_value函数完成格式到业务类型的转换,把辅助枚举映射为Option<String>。- 仅为
Cat变体指定反序列化逻辑,其他变体保持默认行为,改动最小。
运行代码会输出:
[Cat(Some("Meow")), Cat(None), Dog("Bark"), Bird]
如果不想额外定义辅助枚举,也可以直接处理原始JSON值(可读性稍差):
use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; fn cat_from_value<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> where D: Deserializer<'de>, { let value = Value::deserialize(deserializer)?; match value { Value::String(s) if s == "Cat" => Ok(None), Value::Object(mut obj) => { obj.remove("Cat") .map(|v| v.as_str().map(|s| s.to_string()).ok_or_else(|| { serde::de::Error::custom("Cat value must be a string") })) .unwrap_or_else(|| Ok(None)) } _ => Err(serde::de::Error::custom("invalid format for Cat variant")), } } // Animal枚举和main函数同上述代码
内容的提问来源于stack exchange,提问作者Ian Joiner
相关产品推荐
相关产品推荐

