You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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()))
    }
}

工作原理

  1. 拦截枚举反序列化:通过实现deserialize_enum方法,让Serde处理无标签枚举时调用该方法,而非deserialize_any。
  2. 逐个尝试变体:保存原始输入后,遍历枚举的所有变体,为每个变体创建临时反序列化器,尝试解析。
  3. 复用结构体解析逻辑:借助EnumAccess和VariantAccess的实现,将变体的解析转发到已有的deserialize_struct逻辑上。
  4. 处理结果:如果某个变体解析成功,标记输入已消耗并返回结果;所有变体尝试失败则返回错误。

使用方式

现在可以直接用自定义反序列化器解析无标签枚举,无需外部重复代码:

let input = "key_value_key2_value2";
let result: MyEnum = my_deserializer::from_str(input)?;

内容的提问来源于stack exchange,提问作者Gustav Wengel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:45:59