Rust中如何通用地将u32值反序列化为枚举Vec?
问题:将JSON的u32 Flags字段反序列化为Rust的Option<Vec>
需求
需要将JSON中u32类型的Flags字段,反序列化为Rust结构体中的Option<Vec<FooFlag>>类型字段。例如JSON中Flags:20需转换为vec,同时要实现通用逻辑,可复用在多个类似结构体上。
示例与定义
JSON输入示例
{ "Account": "r...", "Flags": 20, ... }
Rust目标结构体
#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all(serialize = "PascalCase", deserialize = "snake_case"))] pub struct Foo<'a> { account: &'a str, flags: Option<Vec<FooFlag>> }
FooFlag枚举定义
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Display, AsRefStr)] pub enum FooFlag { Example1 = 5, Example2 = 15, ... }
现有待完善代码
当前已编写的转换模块:
mod flag_conversion { use alloc::vec::Vec; use serde::{Deserializer, Serializer, Deserialize}; fn serialize<'a, T, S>(flags: &'a Option<Vec<T>>, s: S) -> Result<S::Ok, S::Error> where T: Into<u32>, S: Serializer, { s.serialize_option( { if let Some(flags) = &flags { let transaction_flags: Vec<u32> = flags.into_iter().map(|&flag| { let f = flag; let n = f.into(); n }).collect(); transaction_flags.iter().sum::<u32>() } else { 0 } } ) } fn deserialize<'de, D>(d: D) -> Result<D::Ok, D::Error> where D: Deserializer<'de>, { let specified_flags = u32::deserialize(d).unwrap(); d.deserialize_u32( { if specified_flags == 0 { None } else { todo!() // convert to `Vec` of `...Flag` enum } } ) } }
结构体中使用方式:
#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all(serialize = "PascalCase", deserialize = "snake_case"))] pub struct Foo<'a> { account: &'a str, #[serde(with = "flag_conversion")] flags: Option<Vec<FooFlag>> }
完善后的通用实现
1. 定义通用Trait
首先定义一个Trait,让所有Flag枚举实现该Trait以提供通用操作:
pub trait FlagU32 { // 返回枚举的所有变体 fn all_variants() -> Vec<Self> where Self: Sized; // 返回变体对应的u32数值 fn value(&self) -> u32; }
2. 为FooFlag实现Trait
impl FlagU32 for FooFlag { fn all_variants() -> Vec<Self> { vec![FooFlag::Example1, FooFlag::Example2] // 列出所有枚举变体 } fn value(&self) -> u32 { *self as u32 } }
3. 完善flag_conversion模块
mod flag_conversion { use alloc::vec::Vec; use serde::{Deserializer, Serializer, de::Error}; use super::FlagU32; // 序列化逻辑:将Option<Vec<T>>转换为u32求和 pub fn serialize<'a, T, S>(flags: &'a Option<Vec<T>>, s: S) -> Result<S::Ok, S::Error> where T: FlagU32, S: Serializer, { let sum = flags.as_ref() .map(|flags_list| flags_list.iter().map(|flag| flag.value()).sum::<u32>()) .unwrap_or(0); s.serialize_u32(sum) } // 反序列化逻辑:将u32拆分为对应的枚举变体列表 pub fn deserialize<'de, T, D>(d: D) -> Result<Option<Vec<T>>, D::Error> where T: FlagU32, D: Deserializer<'de>, { let specified_flags = u32::deserialize(d)?; // 0值对应None if specified_flags == 0 { return Ok(None); } let mut remaining = specified_flags; let mut result = Vec::new(); // 遍历所有枚举变体,匹配数值 for variant in T::all_variants() { let val = variant.value(); if val != 0 && remaining >= val { remaining -= val; result.push(variant); } } // 若剩余值不为0,说明存在无法识别的flags,返回错误 if remaining != 0 { return Err(D::Error::custom(format!("无法识别的flags值: {}", remaining))); } Ok(Some(result)) } }
4. 适配按位掩码场景(可选)
如果你的flags是按位掩码(每个枚举变体对应一个独立比特位,例如Example1=4(0b100)、Example2=16(0b10000),按位或得20),可修改反序列化中的匹配逻辑:
// 替换deserialize中的循环部分 for variant in T::all_variants() { let val = variant.value(); if val != 0 && (specified_flags & val) != 0 { result.push(variant); } } // 检查是否存在未知比特位 let all_known_flags: u32 = T::all_variants().iter().map(|f| f.value()).sum(); if (specified_flags & !all_known_flags) != 0 { return Err(D::Error::custom(format!("存在未知flags比特位: {}", specified_flags & !all_known_flags))); }
使用说明
所有需要转换的Flag枚举只需实现FlagU32 trait,在结构体字段上添加#[serde(with = "flag_conversion")]即可复用该通用逻辑。
内容的提问来源于stack exchange,提问作者LimpidCrypto
相关产品推荐
相关产品推荐

