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

如何从含Vec<T>的JSON直接反序列化指定symbol的T对象?

解决方案:无中间Vec分配的JSON目标元素提取

要实现不分配Vec<serde_json::Value>中间内存,直接从JSON的symbols数组中提取目标T结构体,核心思路是手动实现Serde的Deserialize trait,逐层遍历JSON结构,仅处理必要字段,跳过无关内容。

完整代码实现

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

#[derive(Debug)]
struct T {
    symbol: String,
    my_symbol_specific_key: u32,
}

// 顶层JSON对象的访问器:负责找到"symbols"键并处理数组
struct TopLevelVisitor;

impl<'de> Visitor<'de> for TopLevelVisitor {
    type Value = T;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("JSON对象,包含'symbols'数组字段")
    }

    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        // 遍历顶层键值对,仅处理"symbols"字段
        while let Some((key, value)) = map.next_entry()? {
            if key == "symbols" {
                // 将数组交给专门的序列访问器处理
                return value.deserialize_seq(SymbolsArrayVisitor)?;
            }
            // 跳过其他所有字段,不分配内存
            de::IgnoredAny::deserialize(value)?;
        }

        Err(de::Error::missing_field("symbols"))
    }
}

// symbols数组的访问器:逐个遍历元素,寻找目标对象
struct SymbolsArrayVisitor;

impl<'de> Visitor<'de> for SymbolsArrayVisitor {
    type Value = T;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("存储symbol对象的JSON数组")
    }

    fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
    where
        S: SeqAccess<'de>,
    {
        // 定义种子:用于检查单个数组元素是否为目标对象
        struct TargetSymbolSeed;

        impl<'de> de::DeserializeSeed<'de> for TargetSymbolSeed {
            type Value = Option<T>;

            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where
                D: Deserializer<'de>,
            {
                deserializer.deserialize_map(TargetSymbolVisitor)
            }
        }

        // 单个symbol对象的访问器:检查symbol字段,仅反序列化目标对象
        struct TargetSymbolVisitor;

        impl<'de> Visitor<'de> for TargetSymbolVisitor {
            type Value = Option<T>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("包含symbol字段的JSON对象")
            }

            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut target_symbol = None;
                let mut specific_key = None;

                // 遍历当前对象的键值对
                while let Some((key, value)) = map.next_entry()? {
                    match key.as_str() {
                        "symbol" => {
                            target_symbol = Some(value.deserialize()?);
                        }
                        "my_symbol_specific_key" => {
                            // 仅当确认是目标symbol时,才反序列化该字段
                            if target_symbol.as_deref() == Some("MY_SYMBOL") {
                                specific_key = Some(value.deserialize()?);
                            } else {
                                de::IgnoredAny::deserialize(value)?;
                            }
                        }
                        // 跳过所有其他无关字段
                        _ => de::IgnoredAny::deserialize(value)?,
                    }
                }

                // 判断是否找到目标对象
                match target_symbol {
                    Some(s) if s == "MY_SYMBOL" => Ok(Some(T {
                        symbol: s,
                        my_symbol_specific_key: specific_key.ok_or_else(|| {
                            de::Error::missing_field("my_symbol_specific_key")
                        })?,
                    })),
                    _ => Ok(None),
                }
            }
        }

        // 逐个处理数组元素,找到目标就返回
        while let Some(result) = seq.next_element_seed(TargetSymbolSeed)? {
            if let Some(target) = result {
                return Ok(target);
            }
        }

        Err(de::Error::custom("未在symbols数组中找到symbol为'MY_SYMBOL'的对象"))
    }
}

// 封装反序列化逻辑的函数
fn extract_my_symbol(json_str: &str) -> Result<T, serde_json::Error> {
    struct TWrapper(T);

    impl<'de> Deserialize<'de> for TWrapper {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            let target = deserializer.deserialize_map(TopLevelVisitor)?;
            Ok(TWrapper(target))
        }
    }

    let wrapper: TWrapper = serde_json::from_str(json_str)?;
    Ok(wrapper.0)
}

// 测试示例
fn main() -> Result<(), serde_json::Error> {
    let json = r#"
    {
        "symbols": [
            { "symbol": "OTHER_SYMBOL", "other_symbol_specific_key": 2 },
            { "symbol": "MY_SYMBOL", "my_symbol_specific_key": 1 },
            { "symbol": "ANOTHER_SYMBOL", "another_key": 3 }
        ],
        "other_top_level_key": "ignore this"
    }
    "#;

    let target = extract_my_symbol(json)?;
    println!("找到目标对象: {:?}", target);
    assert_eq!(target.symbol, "MY_SYMBOL");
    assert_eq!(target.my_symbol_specific_key, 1);

    Ok(())
}

核心逻辑说明

  1. 顶层访问器(TopLevelVisitor):

    • 遍历JSON顶层对象的键值对,仅处理symbols字段,其余字段用de::IgnoredAny跳过,完全不分配内存。
    • 找到symbols后,将其序列交给SymbolsArrayVisitor处理。
  2. 数组访问器(SymbolsArrayVisitor):

    • 逐个遍历数组元素,每个元素通过TargetSymbolSeed传递给TargetSymbolVisitor检查。
    • 一旦找到目标对象,立即终止遍历并返回结果,无需加载整个数组到内存。
  3. 对象访问器(TargetSymbolVisitor):

    • 遍历单个symbol对象的字段,先读取symbol字段判断是否为目标值。
    • 仅当确认是目标MY_SYMBOL时,才反序列化my_symbol_specific_key字段;否则所有字段都跳过,不分配额外内存。

优势

  • 零中间Vec分配:不会将整个symbols数组加载为Vec<serde_json::Value>,仅为目标对象分配必要的内存。
  • 高效遍历:找到目标后立即终止反序列化过程,无需处理后续元素。
  • 内存友好:尤其适合处理包含大量元素的symbols数组,大幅降低内存占用。

内容的提问来源于stack exchange,提问作者Ahmet Yazıcı

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:14:51