Anonymous Person
Anonymous Person

Reputation: 1538

Trouble with reqwest: Unable to deserialize JSON response + `match` issues

I am learning Rust and it's web api support. I am working on a simple project which does a web API call. Here's the intuition:

(It's all about getting Sprint dates from an Azure DevOps project)

  1. In src/getlatestsprint.rs I have a struct that has fields called "date" and "sprint". They're both String type.
pub struct Sprint {
    date: String,
    sprint: String,
}
  1. I am then doing an impl on it and I am expecting it to return the Sprint itself, and it's supposed to have the date and the sprint number. FYI, what it is returning in the code is just placeholder.
  2. The trouble I am having is with the response (not this block).
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. All this works. get_request.status() is 200 and get_request.text() is Ok(JSON_respose). The trouble starts from here:
                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"),
        })
    }
}

My intention is to return the latest date and sprint got from the response as a Struct.

What am I doing wrong here?

EDIT Sample JSON response:

{ 
    "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/…"
        } 
    ] 
}

Upvotes: 2

Views: 3496

Answers (2)

Anonymous Person
Anonymous Person

Reputation: 1538

So I managed to get what I wanted. I wasn't aware that to work with nested JSON, I had to create a Struct for each of the nested keys. Not just that, I had to even match the case of the field that I am getting the response of. Weird, but OK. I am pretty sure there's an easier way ti do this, but I was able to resolve it myself.

Here are the Structs that I created.

#[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,
}

Upvotes: 2

MeetTitan
MeetTitan

Reputation: 3568

We can serialize and deserialize directly to a custom struct using serde. Let's start by deriving Serialize and Deserialize for Sprint:

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

Then we can clean up the implementation:

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)
    }
}

Upvotes: 1

Related Questions