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

如何在Rust枚举自定义反序列化中复用默认反序列化逻辑?

兼容新旧YAML格式的Rust枚举反序列化方案

要同时支持Rust生成的标准枚举格式和Python遗留的POISON(X)格式,无需重写Serde的默认反序列化逻辑,只需通过自定义反序列化函数+回退解析的方式实现:

步骤1:修改枚举定义

给Symbol添加Deserialize派生,并通过#[serde(deserialize_with)]指定自定义反序列化函数:

use serde::{Serialize, Deserialize, de::Deserializer};
use std::str::FromStr;

#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[serde(deserialize_with = "deserialize_symbol")]
pub enum Symbol {
    #[serde(alias = "BOW")]
    Bow,
    #[serde(alias = "SWORD")]
    Sword,
    Poison(u8),
    // 新增其他变体只需同步后续match分支
}

步骤2:实现自定义反序列化函数

函数逻辑是优先用Serde默认逻辑解析Rust格式,失败则尝试解析Python遗留的字符串格式:

fn deserialize_symbol<'de, D>(deserializer: D) -> Result<Symbol, D::Error>
where
    D: Deserializer<'de>,
{
    // 先尝试默认反序列化(处理Rust生成的`Bow`/`Poison: 10`格式)
    match Symbol::deserialize(deserializer) {
        Ok(symbol) => Ok(symbol),
        Err(_) => {
            // 默认解析失败,尝试按字符串处理Python遗留格式
            let s = String::deserialize(deserializer)?;
            match s.to_uppercase().as_str() {
                "BOW" => Ok(Symbol::Bow),
                "SWORD" => Ok(Symbol::Sword),
                s if s.starts_with("POISON(") && s.ends_with(')') => {
                    // 提取括号内的数值并转换为u8
                    let num_str = s.trim_start_matches("POISON(").trim_end_matches(')');
                    let num = u8::from_str(num_str)
                        .map_err(|_| serde::de::Error::custom(format!("无效的Poison值: {}", num_str)))?;
                    Ok(Symbol::Poison(num))
                }
                _ => Err(serde::de::Error::custom(format!("未知Symbol: {}", s))),
            }
        }
    }
}

关键优势

  • 完全复用Serde官方实现的默认反序列化逻辑,避免自己手动编写复杂的枚举解析代码
  • 仅在默认解析失败时触发旧格式兼容逻辑,不影响新格式的解析效率
  • 字符串转大写处理,兼容Python中大小写不一的序列化结果(如Poison(10)或POISON(10))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:29:53