如何使用Serde JSON处理JSON顶层动态键名问题
解决Rust中JSON顶层动态键名的问题
针对你遇到的顶层动态键(如Time Series (5min),其中时间间隔动态变化)的问题,以下是两种实用解决方案:
前提准备
先明确你的JSON结构和基础结构体定义(模拟示例):
示例JSON
{ "Meta Data": { "1. Information": "Intraday (5min) open, high, low, close prices", "2. Symbol": "IBM", "3. Last Refreshed": "2024-05-20 16:00:00", "4. Interval": "5min", "5. Output Size": "Compact", "6. Time Zone": "US/Eastern" }, "Time Series (5min)": { "2024-05-20 16:00:00": { "1. open": "150.00", "2. high": "150.50", "3. low": "149.80", "4. close": "150.20", "5. volume": "10000" } } }
基础结构体
use serde::Deserialize; use std::collections::HashMap; #[derive(Deserialize, Debug)] struct MetaData { #[serde(rename = "1. Information")] information: String, #[serde(rename = "2. Symbol")] symbol: String, #[serde(rename = "3. Last Refreshed")] last_refreshed: String, #[serde(rename = "4. Interval")] interval: String, #[serde(rename = "5. Output Size")] output_size: String, #[serde(rename = "6. Time Zone")] time_zone: String, } #[derive(Deserialize, Debug)] struct PriceData { #[serde(rename = "1. open")] open: String, #[serde(rename = "2. high")] high: String, #[serde(rename = "3. low")] low: String, #[serde(rename = "4. close")] close: String, #[serde(rename = "5. volume")] volume: String, }
方案一:自定义反序列化(推荐)
通过实现Deserialize trait,将动态键的解析逻辑封装起来,直接解析为类型安全的结构体:
#[derive(Debug)] struct StockData { meta_data: MetaData, interval: String, time_series: HashMap<String, PriceData>, } impl<'de> Deserialize<'de> for StockData { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { // 将整个JSON解析为HashMap,处理动态键 let mut map: HashMap<String, serde_json::Value> = HashMap::deserialize(deserializer)?; // 提取并解析元数据 let meta_data_value = map.remove("Meta Data") .ok_or_else(|| serde::de::Error::missing_field("Meta Data"))?; let meta_data: MetaData = serde_json::from_value(meta_data_value)?; // 匹配以"Time Series ("开头的动态键 let (time_series_key, time_series_value) = map.into_iter() .find(|(key, _)| key.starts_with("Time Series (")) .ok_or_else(|| serde::de::Error::missing_field("Time Series (*)"))?; // 提取时间间隔(如从"Time Series (5min)"中取出"5min") let interval = time_series_key.strip_prefix("Time Series (") .and_then(|s| s.strip_suffix(")")) .ok_or_else(|| serde::de::Error::custom("Invalid time series key format"))? .to_string(); // 解析时间序列数据 let time_series: HashMap<String, PriceData> = serde_json::from_value(time_series_value)?; Ok(Self { meta_data, interval, time_series, }) } } // 使用示例 fn main() -> Result<(), Box<dyn std::error::Error>> { let json_str = r#"[你的JSON字符串]"#; let stock_data: StockData = serde_json::from_str(json_str)?; println!("{:#?}", stock_data); Ok(()) }
方案二:手动解析HashMap(快速原型)
如果不想写自定义反序列化,可以先将JSON解析为HashMap<String, serde_json::Value>,再手动提取并转换数据:
fn main() -> Result<(), Box<dyn std::error::Error>> { let json_str = r#"[你的JSON字符串]"#; // 解析为原始HashMap let raw_map: HashMap<String, serde_json::Value> = serde_json::from_str(json_str)?; // 解析元数据 let meta_data: MetaData = serde_json::from_value(raw_map["Meta Data"].clone())?; // 查找时间序列动态键 let time_series_key = raw_map.keys() .find(|k| k.starts_with("Time Series (")) .ok_or("No time series key found")?; // 解析时间序列数据 let time_series: HashMap<String, PriceData> = serde_json::from_value(raw_map[time_series_key].clone())?; println!("Meta Data: {:#?}", meta_data); println!("Time Series ({}): {:#?}", meta_data.interval, time_series); Ok(()) }
依赖配置
确保Cargo.toml中添加serde相关依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
内容的提问来源于stack exchange,提问作者Dansekongen
相关产品推荐
相关产品推荐

