如何在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
相关产品推荐
相关产品推荐

