1

I'm trying to write an API wrapper with Serde. I'm getting this error when trying to run my program:

Err(Error { kind: Json(Error("invalid type: map, expected a sequence", line: 1, column: 2)), url: None })

It sounds like Serde is encountering a { where it expects a [ based on the way my models are currently setup, but maybe I'm off base with this theory.

The JSON returned directly from the endpoint is:

[
  {
    "title": "Pad Abort Test",
    "event_date_utc": "2015-05-06T13:00:00Z",
    "event_date_unix": 1430917200,
    "flight_number": null,
    "details": "Crew Dragon tests launch abort system, which can provide astronauts with escape capability all the way to orbit.",
    "links": {
      "reddit": "https:\/\/www.reddit.com\/r\/spacex\/comments\/3527zv\/official_video_pad_abort_test_2015\/",
      "article": "https:\/\/spaceflightnow.com\/2015\/04\/21\/dragon-pad-abort-test-set-for-early-may\/",
      "wikipedia": "https:\/\/en.wikipedia.org\/wiki\/Pad_abort_test"
    }
  },
  {
    "title": "SpaceX Awarded Commercial Crew Contract",
    "event_date_utc": "2014-09-16T01:00:00Z",
    "event_date_unix": 1410829200,
    "flight_number": null,
    "details": "NASA awards $2.6 billion SpaceX contract to fly American astronauts.",
    "links": {
      "reddit": null,
      "article": "https:\/\/www.washingtonpost.com\/news\/the-switch\/wp\/2014\/09\/16\/nasa-awards-space-contract-to-boeing-and-spacex\/?utm_term=.d6388390d071",
      "wikipedia": null
    }
  }
]

I know my code is wrong - I just don't know how to fix it. Here it is:

models.rs

#[derive(Debug, Deserialize)]
pub struct HistoricalEvents {
    items: Vec<HistoricalEvent>,
}

#[derive(Debug, Deserialize)]
pub struct HistoricalEvent {
    title: String,
    event_date_utc: String,
    event_date_unix: u64,
    flight_number: Option<u32>,
    details: Option<String>,
    links: Links,
}

#[derive(Debug, Deserialize)]
struct Links {
    reddit: Option<String>,
    article: Option<String>,
    wikipedia: Option<String>,
}

part of lib.rs

pub fn history(
    start_date: Option<String>,
    end_date: Option<String>,
    flight_number: Option<u32>,
    sort_dir: Option<SortDir>,
) -> Result<HistoricalEvents, failure::Error> {
    let mut params = HashMap::new();

    start_date.and_then(|string| params.insert("start", string));

    end_date.and_then(|string| params.insert("end", string));

    flight_number.and_then(|num| params.insert("flight_number", num.to_string()));

    sort_dir.and_then(|dir| params.insert("order", dir.to_string()));

    return send_request("info/history", Some(params));
}

fn send_request<T>(
    endpoint: &str,
    params: Option<HashMap<&str, String>>,
) -> Result<T, failure::Error>
where
    T: DeserializeOwned,
{
    let base = &("https://api.spacexdata.com/v2/".to_owned() + endpoint);
    let url = match params {
        Some(map) => Url::parse_with_params(base, map),
        None => Url::parse(base),
    };
    let client = Client::new();
    let mut request = client.get(url.unwrap().as_str());
    let mut response = request.send()?;
    Ok(response.json()?)
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
kibowki
  • 4,206
  • 16
  • 48
  • 74
  • Don't use `and_then` for that; use `if let` instead – Shepmaster Jul 18 '18 at 18:46
  • I believe your question is answered by the answers of [Rust Deserialize JSON with top level array](https://stackoverflow.com/q/44610594/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jul 18 '18 at 18:49
  • TL;DR the duplicate: Return a `Vec`; your JSON has no object with the key `items`. – Shepmaster Jul 18 '18 at 18:50
  • Ahh, oops - that did it, thanks. We can mark this question as already answered. Also switched to using `if let` instead - why is `if let` better than `and_then` in this scenario? – kibowki Jul 18 '18 at 18:55
  • `and_then` is used to *transform* the `Option` from one thing to another when the closure returns another `Option`. `insert` happens to return an `Option` (the old value). `if let` is appropriate for side-effects, which is what you are doing. – Shepmaster Jul 18 '18 at 18:58
  • It's also idiomatic Rust to not have the explicit return at the end of a block. Just `send_request("info/history", Some(params))` – Shepmaster Jul 18 '18 at 18:59

0 Answers0