如何用Serde实现自定义反序列化器解析untagged enum的非自描述格式
让Serde自定义反序列化器支持无标签枚举
问题背景
你实现的自定义Serde反序列化器可正常解析结构体,但处理#[serde(untagged)]标记的枚举时,会直接调用deserialize_any方法,无法复用结构体的解析逻辑,只能在外部重复编写逐个尝试解析变体的代码。
解决方案
通过实现deserialize_enum方法替代依赖deserialize_any,在反序列化器内部完成逐个变体的尝试解析逻辑,无需外部重复代码。
修改后的代码
首先调整反序列化器的核心实现,移除forward_to_deserialize_any!中的enum项,新增deserialize_enum方法,并实现配套的EnumAccess和VariantAccess:
use serde::{de, Deserialize}; use serde::de::{MapAccess, Visitor, EnumAccess, VariantAccess, DeserializeSeed}; use serde::de::value::MapDeserializer; #[derive(Debug)] pub struct Deserializer<'de> { input: &'de str, } impl<'de> Deserializer<'de> { pub fn from_str(input: &'de str) -> Self { Deserializer { input } } } impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = crate::Error; fn deserialize_struct<V>( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result<V::Value> where V: Visitor<'de>, { let map = crate::parser::emission_factor_id_to_map(self.input, fields)?; let serializer = MapDeserializer::new(map.into_iter()); self.input = ""; visitor.visit_map(serializer) } fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de>, { Err(crate::Error::Message( "Unexpected call to deserialize_any".to_string(), )) } // 新增deserialize_enum方法,处理无标签枚举 fn deserialize_enum<V>( self, _name: &'static str, variants: &'static [&'static str], visitor: V, ) -> Result<V::Value> where V: Visitor<'de>, { let original_input = self.input; // 遍历所有枚举变体,逐个尝试解析 for variant in variants { let mut temp_deser = Deserializer::from_str(original_input); match visitor.visit_enum(TryVariantAccess { deserializer: &mut temp_deser, variant_name: variant, }) { Ok(value) => { self.input = ""; // 标记输入已消耗 return Ok(value); } Err(_) => continue, } } Err(crate::Error::Message("No matching enum variant found".to_string())) } // 移除forward_to_deserialize_any中的enum项 forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map identifier ignored_any } } // 实现EnumAccess,用于尝试单个变体的解析 struct TryVariantAccess<'a, 'de> { deserializer: &'a mut Deserializer<'de>, variant_name: &'static str, } impl<'a, 'de> EnumAccess<'de> for TryVariantAccess<'a, 'de> { type Error = crate::Error; type Variant = TryVariantAccessInner<'a, 'de>; fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: DeserializeSeed<'de>, { // 尝试反序列化当前变体对应的类型 let variant_value = seed.deserialize(self.deserializer)?; Ok((variant_value, TryVariantAccessInner { deserializer: self.deserializer })) } } // 实现VariantAccess,处理newtype类型的枚举变体 struct TryVariantAccessInner<'a, 'de> { deserializer: &'a mut Deserializer<'de>, } impl<'a, 'de> VariantAccess<'de> for TryVariantAccessInner<'a, 'de> { type Error = crate::Error; fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value> where T: DeserializeSeed<'de>, { // 直接复用结构体的解析逻辑 seed.deserialize(self.deserializer) } // 以下方法根据你的枚举类型调整,这里假设只支持newtype变体 fn unit_variant(self) -> Result<()> { Err(crate::Error::Message("Unit variants are not supported".to_string())) } fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value> where V: Visitor<'de>, { Err(crate::Error::Message("Tuple variants are not supported".to_string())) } fn struct_variant<V>( self, _fields: &'static [&'static str], visitor: V, ) -> Result<V::Value> where V: Visitor<'de>, { Err(crate::Error::Message("Struct variants are not supported".to_string())) } }
工作原理
- 拦截枚举反序列化:通过实现
deserialize_enum方法,让Serde处理无标签枚举时调用该方法,而非deserialize_any。 - 逐个尝试变体:保存原始输入后,遍历枚举的所有变体,为每个变体创建临时反序列化器,尝试解析。
- 复用结构体解析逻辑:借助
EnumAccess和VariantAccess的实现,将变体的解析转发到已有的deserialize_struct逻辑上。 - 处理结果:如果某个变体解析成功,标记输入已消耗并返回结果;所有变体尝试失败则返回错误。
使用方式
现在可以直接用自定义反序列化器解析无标签枚举,无需外部重复代码:
let input = "key_value_key2_value2"; let result: MyEnum = my_deserializer::from_str(input)?;
内容的提问来源于stack exchange,提问作者Gustav Wengel
相关产品推荐
相关产品推荐

