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

Rust Serde自定义二进制格式反序列化实现难题求助

问题描述

我实现了一种无类型信息的原始二进制自定义格式Serializer,示例如下:

#[derive(serde::Deserialize)]
struct MyStruct { a: u32, b:u32 }

该结构体以2个大端序32位原始值存储。但反序列化时遇到困难,不清楚deserialize_struct方法的实现逻辑:

struct MyDeserializer<'de> {
  input: &'de [u8],
  offset: usize,
}

impl<'de, 'a> serde::Deserializer<'de> for &'a mut MyDeserializer<'de> {
  fn deserialize_struct<V>(self, _name: &'static str, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
  where V: serde::de::Visitor<'de>
  {
    // 此处应如何实现?
  }
}

查看Deserialize派生宏展开后,打算为MyDeserializer实现SeqAccess,通过next_element_seed中的T反序列化下一段数据,最初思路如下:

impl<'de> SeqAccess<'de> for Deserializer<'de> {
    type Error = super::Error;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: serde::de::DeserializeSeed<'de>
    {
        let len = std::mem::size_of::<T::Value>();
        let start = self.offset;
        self.offset += len;
        Ok(Some(T::Value::from_be_bytes(self.input[start..self.offset])))
    }

    fn size_hint(&self) -> Option<usize> {
        Some(self.input.len() - self.offset)
    }
}

但因T::Value未受约束,无法调用from_be_bytes方法。之后尝试使用num-traits的FromBytes trait修改代码:

fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
    T: serde::de::DeserializeSeed<'de>,
    T::Value: FromBytes,
{
    let len = std::mem::size_of::<T::Value>();
    let start = self.offset;
    self.offset += len;
    let value: T::Value = FromBytes::from_be_bytes(&self.input[start..self.offset]);
    Ok(Some(value))
}

但该方法约束比trait定义更严格,无法实现,目前陷入困境。

解决方案

核心思路是不在SeqAccess中直接解析类型,而是让seed通过子反序列化器自行处理原始类型的反序列化,具体实现步骤如下:

  1. 实现deserialize_struct方法,将结构体反序列化委托给序列访问(因为二进制格式按字段顺序连续存储、无字段名,结构体可当作序列处理):
impl<'de, 'a> serde::Deserializer<'de> for &'a mut MyDeserializer<'de> {
    type Error = serde::de::value::Error; // 可自定义Error类型,此处用serde内置实现

    // 必须实现的通用反序列化方法,委托给序列反序列化
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: serde::de::Visitor<'de>,
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_struct<V>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value, Self::Error>
    where
        V: serde::de::Visitor<'de>,
    {
        // 结构体按字段顺序存储,直接以序列形式访问
        visitor.visit_seq(self)
    }

    // 实现u32类型的反序列化(对应示例中的结构体字段类型)
    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: serde::de::Visitor<'de>,
    {
        // 检查剩余字节是否足够
        if self.offset + 4 > self.input.len() {
            return Err(serde::de::Error::invalid_length(
                self.input.len() - self.offset,
                &"at least 4 bytes for u32"
            ));
        }
        // 截取对应字节并解析为大端序u32
        let bytes = &self.input[self.offset..self.offset+4];
        self.offset += 4;
        let value = u32::from_be_bytes(bytes.try_into().unwrap());
        visitor.visit_u32(value)
    }

    // 若需要支持其他数值类型(如u16、u64等),需实现对应的deserialize_xxx方法
    // fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error> { ... }
    // ...
}
  1. 为MyDeserializer实现SeqAccess,在next_element_seed中将当前反序列化器传递给seed,由seed调用对应类型的反序列化逻辑:
impl<'de> serde::de::SeqAccess<'de> for &mut MyDeserializer<'de> {
    type Error = serde::de::value::Error;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
    where
        T: serde::de::DeserializeSeed<'de>,
    {
        // 无剩余字节时返回None
        if self.offset >= self.input.len() {
            return Ok(None);
        }
        // 让seed使用当前反序列化器解析下一个元素
        seed.deserialize(&mut **self).map(Some)
    }

    fn size_hint(&self) -> Option<usize> {
        // 按单字段4字节估算剩余元素数量,若支持多类型可设为None或更精确计算
        Some((self.input.len() - self.offset) / 4)
    }
}
  1. 关键说明:
  • 原始类型的反序列化逻辑需在Deserializer的deserialize_xxx方法中实现,结构体字段的反序列化器(即seed)会自动调用对应方法完成解析。
  • 这种方式无需对T::Value添加额外约束,完全遵循serde的原生反序列化流程,扩展性更强。
  • 若需要支持更多类型,只需新增对应的deserialize_xxx方法,处理对应字节长度的大端序解析即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 07:27:38