如何从含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(()) }
核心逻辑说明
顶层访问器(
TopLevelVisitor):- 遍历JSON顶层对象的键值对,仅处理
symbols字段,其余字段用de::IgnoredAny跳过,完全不分配内存。 - 找到
symbols后,将其序列交给SymbolsArrayVisitor处理。
- 遍历JSON顶层对象的键值对,仅处理
数组访问器(
SymbolsArrayVisitor):- 逐个遍历数组元素,每个元素通过
TargetSymbolSeed传递给TargetSymbolVisitor检查。 - 一旦找到目标对象,立即终止遍历并返回结果,无需加载整个数组到内存。
- 逐个遍历数组元素,每个元素通过
对象访问器(
TargetSymbolVisitor):- 遍历单个symbol对象的字段,先读取
symbol字段判断是否为目标值。 - 仅当确认是目标
MY_SYMBOL时,才反序列化my_symbol_specific_key字段;否则所有字段都跳过,不分配额外内存。
- 遍历单个symbol对象的字段,先读取
优势
- 零中间Vec分配:不会将整个
symbols数组加载为Vec<serde_json::Value>,仅为目标对象分配必要的内存。 - 高效遍历:找到目标后立即终止反序列化过程,无需处理后续元素。
- 内存友好:尤其适合处理包含大量元素的
symbols数组,大幅降低内存占用。
内容的提问来源于stack exchange,提问作者Ahmet Yazıcı
相关产品推荐
相关产品推荐

