You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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结构体。

修正步骤

  1. 使用正确的根类型反序列化
    把原来的res.json::<Response>().unwrap()改成用Root作为目标类型:

    let root = res.json::<Root>().unwrap();
    
  2. 访问你需要的数据
    现在你可以通过嵌套的结构体访问结果了。比如要获取计算结果的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(),可以用matchif let来优雅处理错误,比如网络请求失败、反序列化失败的情况。
  • 如果你只关心plaintext结果,可以考虑用serde的#[serde(skip)]忽略不需要的字段,减少结构体的复杂度,但对于刚开始学习的阶段,保留完整结构体更便于理解数据结构。

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

火山引擎 最新活动