自定义Rust Deserializer处理带u8判别式的Crc枚举反序列化
手动实现Crc枚举的Serde反序列化
核心思路
你的二进制格式是首字节为判别式,后续为对应长度的校验和字节数组,反序列化的关键逻辑如下:
- 先读取第一个u8判别式,确定目标枚举变体
- 根据判别式读取对应长度的字节,转换成固定大小数组
- 构造对应的Crc枚举实例
具体代码实现
首先确保Cargo.toml中包含依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] }
完整实现代码:
use serde::{Deserialize, Deserializer, de::{Visitor, SeqAccess, Error}}; use std::fmt; #[derive(Debug, PartialEq)] enum Crc { Crc16([u8; 16]), Crc32([u8; 32]), Crc64([u8; 64]), } // 自定义Visitor处理变体区分与数组反序列化 struct CrcVisitor; impl<'de> Visitor<'de> for CrcVisitor { type Value = Crc; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("CRC判别式(u8) + 对应长度的校验和字节数组") } fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: SeqAccess<'de>, { // 第一步:读取判别式 let discriminant: u8 = seq.next_element()?.ok_or_else(|| Error::custom("缺少判别式"))?; // 第二步:根据判别式读取对应长度的字节数组 match discriminant { 0 => { let mut bytes = [0u8; 16]; for i in 0..16 { bytes[i] = seq.next_element()?.ok_or_else(|| Error::custom(format!("Crc16缺失第{}个字节", i)))?; } Ok(Crc::Crc16(bytes)) } 1 => { let mut bytes = [0u8; 32]; for i in 0..32 { bytes[i] = seq.next_element()?.ok_or_else(|| Error::custom(format!("Crc32缺失第{}个字节", i)))?; } Ok(Crc::Crc32(bytes)) } 2 => { let mut bytes = [0u8; 64]; for i in 0..64 { bytes[i] = seq.next_element()?.ok_or_else(|| Error::custom(format!("Crc64缺失第{}个字节", i)))?; } Ok(Crc::Crc64(bytes)) } _ => Err(Error::custom(format!("未知CRC判别式: {}", discriminant))), } } } // 为Crc实现Deserialize trait impl<'de> Deserialize<'de> for Crc { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { // 传入最大序列长度(1个判别式+64个字节),适配所有变体 deserializer.deserialize_tuple(1 + 64, CrcVisitor) } }
关键细节说明
- 判别式映射:示例中用
0对应Crc16、1对应Crc32、2对应Crc64,可根据实际协议调整数值 - 数组读取逻辑:通过循环逐个填充固定大小数组,避开serde原生对数组大小的32限制
- 错误处理:覆盖了判别式缺失、字节缺失、非法判别式等场景,符合serde错误规范
- 序列长度参数:
deserialize_tuple传入1+64是因为最大变体需要1个判别式+64个字节,确保反序列化器能处理所有情况
测试示例
用bincode验证实现逻辑:
use bincode; fn main() { // 测试Crc16:判别式0 + 16个0字节 let test_data = [0u8; 17]; let crc: Crc = bincode::deserialize(&test_data).unwrap(); assert_eq!(crc, Crc::Crc16([0u8; 16])); // 测试Crc64:判别式2 + 64个1字节 let mut crc64_data = vec![2u8]; crc64_data.extend(vec![1u8; 64]); let crc: Crc = bincode::deserialize(&crc64_data).unwrap(); assert_eq!(crc, Crc::Crc64([1u8; 64])); }
内容的提问来源于stack exchange,提问作者Phidelux
相关产品推荐
相关产品推荐

