Rust使用serde_json反序列化API返回JSON数据结构不匹配报错
问题背景
- 目标:抓取OSRS Wiki公开价格接口返回的JSON数据,接口地址为
https://prices.runescape.wiki/api/v1/osrs/latest - 先后尝试两种结构体定义方案,均触发相同反序列化报错,移除
serde_with嵌套解析配置后问题仍未解决 - 运行时报错信息:
Error: reqwest::Error { kind: Decode, source: Error("invalid type: map, expected valid json object", line: 1, column: 8)
原有问题核心代码:
#[derive(Serialize, Deserialize, Debug)] struct Latest { high: u32, highTime: String, low: String, lowTime: String, } #[derive(Serialize, Deserialize, Debug)] struct Data { #[serde(with = "serde_with::json::nested")] data: Vec<Latest>, } #[derive(Serialize, Deserialize, Debug)] struct Datav2 { #[serde(with = "serde_with::json::nested")] data: HashMap<u32, Vec<Latest>>, } #[cfg(not(target_arch = "wasm32"))] #[tokio::main] async fn main() -> Result<(), reqwest::Error> { let res = reqwest::get(url).await?; let response = &res.json::<Datav2>().await?; }
问题根因
代码存在4个核心问题,直接触发反序列化报错:
- 误用
serde_with::json::nested配置:该配置仅用于解析被转义为字符串的嵌套JSON内容,目标接口返回的data字段是原生JSON对象,添加该配置会让serde尝试将原生Map类型按字符串规则解析,直接触发类型不匹配错误 - 嵌套类型定义错误:接口返回的每个数字ID对应的价格条目是单值对象,不是数组,不需要用
Vec<Latest>包裹 - 字段可见性错误:Rust结构体默认字段为私有,serde反序列化需要字段拥有公开可见性,常规实现直接为字段添加
pub修饰即可 - 基础类型定义错误:接口返回的
highTime/lowTime是Unix时间戳数值类型,low字段和high字段一致为无符号整数,均不是字符串类型
正确实现
首先配置Cargo.toml依赖:
[dependencies] reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] }
核心业务代码:
use std::collections::HashMap; use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] struct Latest { // 用Option包裹是因为部分无交易记录的物品会返回null值,避免反序列化失败 pub high: Option<u32>, pub highTime: Option<u64>, pub low: Option<u32>, pub lowTime: Option<u64>, } #[derive(Serialize, Deserialize, Debug)] struct PriceResponse { // 移除错误的serde_with嵌套解析配置,data本身就是ID到价格对象的Map pub data: HashMap<u32, Latest>, } #[cfg(not(target_arch = "wasm32"))] #[tokio::main] async fn main() -> Result<(), reqwest::Error> { let url = "https://prices.runescape.wiki/api/v1/osrs/latest"; let res = reqwest::get(url).await?; let response: PriceResponse = res.json().await?; // 测试读取:打印ID为4151的物品(龙战靴)价格信息 if let Some(item_price) = response.data.get(&4151) { println!("龙战靴最新交易数据:{:?}", item_price); } Ok(()) }
补充说明:serde默认支持驼峰命名字段匹配,不需要额外添加
#[serde(rename_all = "camelCase")]配置即可正常解析接口返回的highTime/lowTime字段。
内容的提问来源于stack exchange,提问作者TrapLordOb
相关产品推荐
相关产品推荐

