json - 从 serde_json 中的非类型化 JSON 中提取数据时如何处理错误?

标签 json rust serde serde-json

我有一个 serde_json::Value我希望它包含一个对象数组。我想从这些对象中提取 2 个值并在任何失败时返回错误。到目前为止,这是我的代码:

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 fails for some reason
                product["stock"].as_str()?.parse::<u32>()?,
            )
        })
        .collect()?
}

当我使用 .unwrap() 时这工作正常,但在将返回类型更改为 Result 之后并用 ? 替换 unwraps我收到以下编译错误:

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[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:11:41
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
   | |                                         ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
...  |
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[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:13:42
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
12 | |                 //as_u64 fails for some reason
13 | |                 product["stock"].as_str()?.parse::<u32>()?,
   | |                                          ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
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[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:13:58
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
12 | |                 //as_u64 fails for some reason
13 | |                 product["stock"].as_str()?.parse::<u32>()?,
   | |                                                          ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
miav@battlestation catbot % cargo run --bin products
   Compiling catbot v0.1.0 (/Users/miav/Documents/Personal/Projects/programming/catbot)
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[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:11:41
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
   | |                                         ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
...  |
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[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:13:42
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
12 | |                 //as_u64 fails for some reason
13 | |                 product["stock"].as_str()?.parse::<u32>()?,
   | |                                          ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
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[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:13:58
   |
8  |           .map(|product| {
   |  ______________-
9  | |             let product = product.as_object()?;
10 | |             (
11 | |                 product["name"].as_str()?.to_owned(),
12 | |                 //as_u64 fails for some reason
13 | |                 product["stock"].as_str()?.parse::<u32>()?,
   | |                                                          ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
14 | |             )
15 | |         })
   | |_________- this function should return `Result` or `Option` to accept `?`
   |
   = help: the trait `FromResidual<Result<Infallible, ParseIntError>>` is not implemented for `(std::string::String, u32)`
   = note: required by `from_residual`

error: aborting due to 5 previous errors

我事先不知道JSON的确切结构,所以无法将其解析成结构体。我所知道的是,它很可能是一组具有 name 的对象。字符串字段和一个 stock整数字段,我想将其提取到 map 中。如果情况并非如此,那么我想返回一个错误。最简单的方法是什么?

更新: 在遵循 Lagerbaer 的建议并进行一些挖掘之后,我想出了以下解决方案。

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' not found"))?
        .as_array()
        .ok_or(anyhow!("'products' is not an array"))?
        .iter()
        .map(|product| -> Result<(String, u32)> {
            let product = product.as_object().unwrap();
            Ok((
                product
                    .get("name")
                    .ok_or(anyhow!("'name' not found"))?
                    .as_str()
                    .ok_or(anyhow!("'name' is not a string"))?
                    .trim()
                    .to_owned(),
                //as_u64 fails for some reason
                product
                    .get("stock")
                    .ok_or(anyhow!("'stock' not found"))?
                    .as_str()
                    .ok_or(anyhow!("'stock' is not a string"))?
                    .parse::<u32>()?,
            ))
        })
        .collect()
}

二手 ok_or()到 map OptionResult , 必须使用 anyhow!宏以使其与 anyhow 结果类型兼容。 事实证明 collect()实际上接受一个迭代器 Result<(String, u32)>产生一个Result<HashMap<String, u32>>具有我想要的确切行为,即返回第一个错误或完整的 HashMap (如果没有错误)。

最佳答案

所以这里有几个问题,编译器会尽力告诉你。

我会先引导您开始,然后我鼓励您尝试单独进行一段时间。

所以第一个错误显然是,as_array 返回一个 Option 而不是 Result,因此,您不能使用? 运算符。

幸运的是,编译器会告诉您该怎么做:Option 有一个您应该调用的方法ok_or。它会将 Option 转换为 Result。如果 OptionSome(value) 你会得到一个 Ok(value),如果 OptionNone,您将得到您在 ok_or 参数中指定的任何错误。

另一个容易发现的错误是:您的函数返回一个 Result,因此返回值显然应该是一个 Result。因此最后一个 ? 应该被删除,因为那个 ? 会接受一个 Result 并将它变成“纯”值(如果结果是 Ok)。

然后剩下的就是弄清楚闭包应该返回什么。也许您可以在修正我上面解释的其他错误后尝试这样做。

关于json - 从 serde_json 中的非类型化 JSON 中提取数据时如何处理错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69057008/

相关文章:

javascript - 访问 JavaScript 对象变量属性

java - 避免在 MOXy (JAXB+JSON) 中创建对象包装器类型/值

rust - 如何通过泛型获取数组结构 (SOA) 中的值?

types - 我如何表示这些数据?

json - 无法解析 `Responder<' _, '_>` 未针对 `rocket_contrib::json::Json<MyStruct>` 实现

javascript - JSON 上的 jQuery $.each

php - 为什么这个 javascript/jQuery JSON 解析代码不起作用?

json - 如何仅为 serde_json 实现自定义序列化?

json - Serde反序列化json文件时缺少字段错误

rust - 如何使用Serde的SeqDeserializer将序列反序列化为自定义类型?