Rust解析WolframAlpha API返回JSON时遭遇reqwest反序列化错误:预期序列,实际得到映射
解决Rust中解析WolframAlpha API JSON时的反序列化panic错误
你遇到的panic问题根源很明确:你告诉serde要反序列化一个数组(Vec<Subpod>),但WolframAlpha返回的JSON根是一个对象。错误信息里的invalid type: map, expected a sequence就是在说这个——JSON里的{}是"map/对象",而你期望的是"sequence/数组"。
问题出在哪?
你定义了pub type Response = Vec<Subpod>;,然后尝试把API返回的JSON反序列化成这个类型,但实际返回的JSON最外层是一个包含queryresult字段的对象,完全匹配你已经定义好的Root结构体。
修正步骤
使用正确的根类型反序列化
把原来的res.json::<Response>().unwrap()改成用Root作为目标类型:let root = res.json::<Root>().unwrap();访问你需要的数据
现在你可以通过嵌套的结构体访问结果了。比如要获取计算结果的plaintext,你需要遍历pods找到目标Pod(比如id为"Result"的),然后取出它的subpods里的内容:举个例子,添加这段代码来打印结果:
// 找到id为"Result"的Pod if let Some(result_pod) = root.queryresult.pods.iter().find(|pod| pod.id == "Result") { // 取出第一个subpod的plaintext if let Some(subpod) = result_pod.subpods.first() { println!("计算结果: {}", subpod.plaintext); } }
完整修正后的代码
use serde::{Deserialize, Serialize}; use reqwest::blocking::get; fn main() { let api = String::from("your_api_key"); // 替换成你的API密钥 let request_url = format!("http://api.wolframalpha.com/v2/query?input=2+2&appid={}&includepodid=Result&format=plaintext&output=json", api); let res = get(request_url).unwrap(); // 反序列化成Root类型,而不是Vec<Subpod> let root = res.json::<Root>().unwrap(); // 提取并打印结果 if let Some(result_pod) = root.queryresult.pods.iter().find(|pod| pod.id == "Result") { if let Some(subpod) = result_pod.subpods.first() { println!("计算结果: {}", subpod.plaintext); } } } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Root { pub queryresult: Queryresult, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Queryresult { pub success: bool, pub error: bool, pub numpods: i64, pub datatypes: String, pub timedout: String, pub timedoutpods: String, pub timing: f64, pub parsetiming: f64, pub parsetimedout: bool, pub recalculate: String, pub id: String, pub host: String, pub server: String, pub related: String, pub version: String, pub inputstring: String, pub pods: Vec<Pod>, pub assumptions: Assumptions, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Pod { pub title: String, pub scanner: String, pub id: String, pub position: i64, pub error: bool, pub numsubpods: i64, pub primary: bool, pub subpods: Vec<Subpod>, pub expressiontypes: Expressiontypes, pub states: Vec<State>, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Subpod { pub title: String, pub plaintext: String, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Expressiontypes { pub name: String, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct State { pub name: String, pub input: String, pub stepbystep: bool, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Assumptions { #[serde(rename = "type")] pub type_field: String, pub template: String, pub count: i64, pub values: Vec<Value>, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Value { pub name: String, pub desc: String, pub input: String, }
额外提示
- 尽量避免在生产代码中过度使用
unwrap(),可以用match或if let来优雅处理错误,比如网络请求失败、反序列化失败的情况。 - 如果你只关心
plaintext结果,可以考虑用serde的#[serde(skip)]忽略不需要的字段,减少结构体的复杂度,但对于刚开始学习的阶段,保留完整结构体更便于理解数据结构。
内容的提问来源于stack exchange,提问作者mvolaric




