Rust中使用serde_json从无类型JSON提取数据时如何处理错误
问题描述
我有一个serde_json::Value类型的值,预期其内部包含一个对象数组,需要从这些对象中提取两个字段的值,任意步骤出错都要返回错误。我当前的代码如下:
use std::collections::HashMap; use anyhow::Result; fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>>{ response["products"] .as_array()?.iter() .map(|product| { let product = product.as_object()?; ( product["name"].as_str()?.to_owned(), // 此处as_u64调用会失败 product["stock"].as_str()?.parse::<u32>()?, ) }) .collect()? }
之前使用.unwrap()时代码可以正常运行,但我将返回值改为Result类型、用?替换unwrap后,出现了如下编译错误:
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` --> src/bin/products.rs:7:20 | 5 | / fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>>{ 6 | | response["products"] 7 | | .as_array()?.iter() | | ^ use `.ok_or(...)?` to provide an error compatible with `Result<HashMap<std::string::String, u32>, anyhow::Error>` 8 | | .map(|product| { ... | 16 | | .collect()? 17 | | } | |_- this function returns a `Result` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<HashMap<std::string::String, u32>, anyhow::Error>` = note: required by `from_residual` error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`) --> src/bin/products.rs:9:46 | 8 | .map(|product| { | ______________- 9 | | let product = product.as_object()?; | | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)` 10 | | ( 11 | | product["name"].as_str()?.to_owned(), ... | 14 | | ) 15 | | }) | |_________- this function should return `Result` or `Option` to accept `?` | = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)` = note: required by `from_residual` error: aborting due to 5 previous errors
我事先不知道JSON的精确结构,所以无法直接解析为结构体。仅确定它大概率是对象数组,每个对象包含字符串类型的name字段和整数类型的stock字段,需要将这两个字段提取为哈希映射,不符合预期结构时就返回错误。
实现方案
use anyhow::{anyhow, Result}; use std::collections::HashMap; fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>> { response .get("products") .ok_or(anyhow!("'products' 字段不存在"))? .as_array() .ok_or(anyhow!("'products' 不是数组类型"))? .iter() .map(|product| -> Result<(String, u32)> { let product = product.as_object().unwrap(); Ok(( product .get("name") .ok_or(anyhow!("'name' 字段不存在"))? .as_str() .ok_or(anyhow!("'name' 不是字符串类型"))? .trim() .to_owned(), // 此处as_u64调用不生效,因此转为字符串后解析 product .get("stock") .ok_or(anyhow!("'stock' 字段不存在"))? .as_str() .ok_or(anyhow!("'stock' 不是字符串类型"))? .parse::<u32>()?, )) }) .collect() }
逻辑说明
- 用
ok_or()方法将serde_json方法返回的Option类型转换为Result类型,通过anyhow!宏生成兼容返回值要求的错误信息 collect()可以直接接收元素为Result<(String, u32)>的迭代器,返回Result<HashMap<String, u32>>,符合预期执行逻辑:遇到第一个错误就直接返回错误,无错误时返回完整的哈希映射
内容的提问来源于stack exchange,提问作者HelloImRandom
相关产品推荐
相关产品推荐

