【问题标题】:Trouble with reqwest: Unable to deserialize JSON response + `match` issuesreqwest 的问题:无法反序列化 JSON 响应 + `match` 问题
【发布时间】:2021-12-10 19:18:03
【问题描述】:

我正在学习 Rust 和它的 web api 支持。我正在做一个简单的项目,该项目执行 Web API 调用。这是直觉:

(一切都是为了从 Azure DevOps 项目中获取 Sprint 日期)

  1. src/getlatestsprint.rs 中,我有一个结构,其中包含名为“日期”和“冲刺”的字段。它们都是字符串类型。
pub struct Sprint {
    date: String,
    sprint: String,
}
  1. 然后我在上面做一个impl,我期待它返回Sprint本身,它应该有datesprint号码。仅供参考,它在代码中返回的只是占位符。
  2. 我遇到的问题是响应(不是这个块)。
impl Sprint {

    pub fn get_sprint_and_date() -> Result<Sprint, reqwest::Error> {
        
        let sprint_url: &str = "https://dev.azure.com/redactedOrg/redactedProj/redactedTeam/_apis/work/teamsettings/iterations?api-version=6.0";

        let pat: &str = "redactedPAT";

        let client = reqwest::blocking::Client::new();

        let get_request = client.get(sprint_url)

                                    .basic_auth(&"None", Some(pat))
                                    .send()?;
  1. 所有这些都有效。 get_request.status()200get_request.text()Ok(JSON_respose)。麻烦从这里开始:
                if get_request.status() == 200 {
            match get_request.text() {
                Ok(v) => {
                    let deserialized_json: HashMap<String, serde_json::Value> = serde_json::from_str(&v).unwrap(); //This I got from another SO post that I was going through to work this out.

                    match deserialized_json.get("value") {
                        Some(des_j) => des_j,
                        None => None, //It says here "expected `&serde_json::Value`, found enum `std::option::Option`" This is where I am lost really.
                    }
                },
                _ => (),
            }
        }


//Placeholder return statement (I know I don't need to write return, but I am just doing it because I am used to it in other languages)
        return Ok(Sprint {
            date: String::from("sdfsd"),
            sprint: String::from("dsfsfsdfsdfsd"),
        })
    }
}

我的意图是将响应中获得的最新日期和冲刺作为结构返回。

我在这里做错了什么?

编辑 示例 JSON 响应:

{ 
    "count": 82, 
    "value": [
        { 
            "id": "redactedID", 
            "name": "Sprint 74", 
            "path": "redactedProjectName\\Sprint 74", 
            "attributes": { 
                "startDate": "2018-10-22T00:00:00Z", 
                "finishDate": "2018-11-04T00:00:00Z", 
                "timeFrame": "past"
            }, 
            "url": "dev.azure.com/redactedOrg/redactedProject/redactedProj/_apis/…"
        } 
    ] 
}

【问题讨论】:

    标签: rust serde reqwest


    【解决方案1】:

    所以我设法得到了我想要的。我不知道要使用嵌套 JSON,我必须为每个嵌套键创建一个 Struct。不仅如此,我什至必须匹配我得到响应的领域的情况。很奇怪,但还可以。我很确定有一种更简单的方法可以做到这一点,但我自己能够解决它。

    这是我创建的结构。

    #[derive(Deserialize, Serialize)]
    struct Response {
        count: u32,
        value: Vec<Value>,
    }
    
    #[derive(Deserialize, Serialize, Debug)]
    struct Value {
        id: String,
        name: String,
        path: String,
        attributes: Attributes,
        url: String,
    }
    
    #[derive(Deserialize, Serialize, Debug)]
    struct Attributes {
        startDate: String,
        finishDate: String,
        timeFrame: String,
    }
    

    【讨论】:

    • 查看我对您的问题的评论,以便进一步清理。不过干得好,请为自己解决这个问题投赞成票:)
    【解决方案2】:

    我们可以使用 serde 直接对自定义结构进行序列化和反序列化。 让我们从为Sprint 导出SerializeDeserialize 开始:

    //This requires the serde crate with derive feature
    use serde::{Serialize, Deserialize};
    #[derive(Deserialize, Serialize)]
    pub struct Sprint {
        date: String,
        sprint: String,
    }
    

    然后我们可以清理实现:

    impl Sprint {
    
        pub fn get_sprint_and_date() -> Result<Sprint, reqwest::Error> {
        
            let sprint_url: &str = "https://dev.azure.com/redactedOrg/redactedProj/redactedTeam/_apis/work/teamsettings/iterations?api-version=6.0";
    
            let pat: &str = "redactedPAT";
    
            let client = reqwest::blocking::Client::new();
    
            let get_request = client.get(sprint_url)
                                    .basic_auth(&"None", Some(pat))
                                    .send()?;
            //This requires the reqwest crate with the json feature
            let sprint: Sprint = get_request.json()?;
            
            Ok(sprint)
        }
    }
    

    【讨论】:

    • 不幸的是,这无济于事。这就是我这样做时得到的结果Err(reqwest::Error { kind: Decode, source: Error("missing field date", line: 1, column: 31215) }) 在这样做之前我应该​​注意什么?我应该说我得到一个嵌套的 JSON 作为响应。它不是扁平的 JSON,所以,我认为按照您的建议将它们放入 sprint Struct 行不通?不过我可能是错的。
    • 你能举一个你期望的json的例子吗?您的服务器响应似乎不包含作为*字段的日期。
    • 没错。响应不包含 date 作为*字段。它包含countvalue。这是否意味着我必须将 Stuct 字段的名称更改为该名称?那么字段内嵌套的 JSON 数据呢?就像,value 有我感兴趣的实际数据。这将如何解释?我的麻烦在于,我将所有这些与 Python 的 Request 模块的功能进行比较,我想这很正常,因为我更习惯了。
    • 示例:``` { "count": 82, "value": [ { "id": "redactedID", "name": "Sprint 74", "path": "redactedProjectName\ \Sprint 74", "attributes": { "startDate": "2018-10-22T00:00:00Z", "finishDate": "2018-11-04T00:00:00Z", "timeFrame": "past" } , "url": "dev.azure.com/redactedOrg/redactedProject/redactedProj/_apis/…" } ] } ```value下的图片多记录
    • 感谢您对此进行调查。我设法解决了。