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

使用Serde自定义数据格式反序列化器时如何捕获任意值?

实现自定义Serde反序列化器的“RawValue”等效功能

核心原理

serde_json的RawValue本质是在反序列化流程中暂停解析,捕获并保留一段未处理的原始输入数据,后续可以将这段数据重新喂给反序列化器完成二次解析。对于自定义格式,要实现等效功能,核心是让你的反序列化器具备两个能力:

  • 能跟踪输入数据的位置,准确截取需要保留的原始片段
  • 能将截取的原始片段重新封装为反序列化器的输入,启动二次解析

具体实现步骤

1. 定义自定义Raw类型

先创建一个类型来存储原始输入片段,同时提供二次解析的方法:

use serde::de::{self, Deserialize, Deserializer};
use std::fmt;

// 自定义Raw类型,保存原始输入的字节切片(根据你的格式选择&str或&[u8])
#[derive(Debug)]
pub struct MyRawValue<'a>(&'a [u8]);

impl<'a> MyRawValue<'a> {
    // 将原始数据重新传给自定义反序列化器,解析为目标类型
    pub fn deserialize_as<T: Deserialize<'a>>(&self) -> Result<T, de::Error> {
        // 假设你的自定义反序列化器是MyDeserializer,实现了from_slice方法
        let mut deserializer = MyDeserializer::from_slice(self.0);
        T::deserialize(&mut deserializer)
    }
}

2. 为Raw类型实现Deserialize trait

通过自定义Visitor来捕获原始数据,关键是让Visitor和你的自定义反序列化器配合,获取未解析的原始片段:

impl<'a> Deserialize<'a> for MyRawValue<'a> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'a>,
    {
        struct RawVisitor;

        impl<'a> Visitor<'a> for RawVisitor {
            type Value = MyRawValue<'a>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid data in my custom format")
            }

            // 根据你的格式类型实现对应的visit方法:文本格式用visit_str,二进制用visit_bytes
            fn visit_str<E: de::Error>(self, raw_str: &'a str) -> Result<Self::Value, E> {
                Ok(MyRawValue(raw_str.as_bytes()))
            }

            // 如果要捕获嵌套结构(比如对象/数组),需要访问反序列化器的位置信息
            fn visit_map<A: de::MapAccess<'a>>(self, mut map: A) -> Result<Self::Value, A::Error> {
                // 这里需要你的自定义MapAccess能获取反序列化器的游标位置
                // 伪代码示例(需要根据你的反序列化器内部结构实现):
                // let start_pos = map.deserializer().cursor_position();
                // // 跳过整个map不解析,只记录边界
                // while map.next_entry::<(), ()>()?.is_some() {}
                // let end_pos = map.deserializer().cursor_position();
                // let raw_slice = map.deserializer().input()[start_pos..end_pos];
                // Ok(MyRawValue(raw_slice))

                // 若暂未实现位置跟踪,可先返回错误提示
                Err(de::Error::custom("capturing raw map requires cursor tracking in your deserializer"))
            }
        }

        // 用deserialize_any触发Visitor的对应方法,适配所有数据类型
        deserializer.deserialize_any(RawVisitor)
    }
}

3. 改造自定义反序列化器,添加位置跟踪能力

这是实现的关键——你的自定义Deserializer必须能跟踪当前解析的位置,才能准确截取原始片段。比如:

// 假设你的自定义反序列化器结构示例
pub struct MyDeserializer<'a> {
    input: &'a [u8],
    cursor: usize, // 跟踪当前解析到的位置
}

impl<'a> MyDeserializer<'a> {
    // 从字节切片创建反序列化器
    pub fn from_slice(input: &'a [u8]) -> Self {
        MyDeserializer { input, cursor: 0 }
    }

    // 暴露当前游标位置
    pub fn cursor_position(&self) -> usize {
        self.cursor
    }

    // 暴露原始输入切片
    pub fn input(&self) -> &'a [u8] {
        self.input
    }

    // ... 其他已有的反序列化方法(比如parse_key、parse_value等)
}

在解析嵌套结构(比如对象)时,你需要在进入结构时记录起始游标,结束时记录结束游标,从而截取这段结构的原始输入。

4. 业务逻辑中使用

现在可以在你的结构体中嵌入MyRawValue,先解析外层字段,再根据字段值决定如何解析原始数据:

#[derive(Deserialize)]
struct MyData<'a> {
    data_type: String,
    raw_content: MyRawValue<'a>,
}

// 使用示例
fn process_input(input: &[u8]) -> Result<(), de::Error> {
    let mut deserializer = MyDeserializer::from_slice(input);
    let my_data: MyData = MyData::deserialize(&mut deserializer)?;

    match my_data.data_type.as_str() {
        "user" => {
            let user: User = my_data.raw_content.deserialize_as()?;
            // 处理User类型
        }
        "product" => {
            let product: Product = my_data.raw_content.deserialize_as()?;
            // 处理Product类型
        }
        _ => return Err(de::Error::custom("unknown data type")),
    }
    Ok(())
}

关键注意事项

  • 原始片段的生命周期必须和输入数据一致,避免悬垂引用
  • 嵌套结构的原始捕获需要精准跟踪结构边界,这取决于你对自定义格式语法的实现深度
  • 如果你的格式有复杂的语法(比如转义字符、注释),截取原始片段时要确保包含完整的合法结构,否则二次解析会失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:01:10