28

I am trying to poll the GitHub API for issues and print them out. To do so, I need to deserialize a nested JSON structure that I receive from a cURL GET request.

I am trying to get the url for all the objects in the items array:

{ "total_count": 4905, "incomplete_results": false, "items": [ { "url": "https://api.github.com/repos/servo/saltfs/issues/789", "repository_url": "https://api.github.com/repos/servo/saltfs", "labels_url": "https://api.github.com/repos/servo/saltfs/issues/789/labels{/name}", "comments_url": "https://api.github.com/repos/servo/saltfs/issues/789/comments", "events_url": "https://api.github.com/repos/servo/saltfs/issues/789/events", "html_url": "https://github.com/servo/saltfs/issues/789", "id": 293260512, "number": 789, "title": "Stop setting $CARGO_HOME to its default value", "user": { "login": "SimonSapin", "id": 291359, "avatar_url": "https://avatars0.githubusercontent.com/u/291359?v=4", "gravatar_id": "", "url": "https://api.github.com/users/SimonSapin", "html_url": "https://github.com/SimonSapin", "followers_url": "https://api.github.com/users/SimonSapin/followers", "following_url": "https://api.github.com/users/SimonSapin/following{/other_user}", "gists_url": "https://api.github.com/users/SimonSapin/gists{/gist_id}", "starred_url": "https://api.github.com/users/SimonSapin/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/SimonSapin/subscriptions", "organizations_url": "https://api.github.com/users/SimonSapin/orgs", "repos_url": "https://api.github.com/users/SimonSapin/repos", "events_url": "https://api.github.com/users/SimonSapin/events{/privacy}", "received_events_url": "https://api.github.com/users/SimonSapin/received_events", "type": "User", "site_admin": false }, "labels": [ { "id": 341722396, "url": "https://api.github.com/repos/servo/saltfs/labels/E-easy", "name": "E-easy", "color": "02e10c", "default": false } ], "state": "open", "locked": false, "assignee": null, "assignees": [ ], "milestone": null, "comments": 0, "created_at": "2018-01-31T18:16:09Z", "updated_at": "2018-01-31T18:16:49Z", "closed_at": null, "author_association": "MEMBER", "body": "In `buildbot/master/files/config/environments.py` we set `CARGO_HOME` to Cargo’s default value. Now that `mach` does not set it (since https://github.com/servo/servo/pull/19395), this has no effect. We can remove these lines.", "score": 1.0 }, { "url": "https://api.github.com/repos/servo/servo/issues/19916", "repository_url": "https://api.github.com/repos/servo/servo", "labels_url": "https://api.github.com/repos/servo/servo/issues/19916/labels{/name}", "comments_url": "https://api.github.com/repos/servo/servo/issues/19916/comments", "events_url": "https://api.github.com/repos/servo/servo/issues/19916/events", "html_url": "https://github.com/servo/servo/issues/19916", "id": 293237180, "number": 19916, "title": "Use a macro to create null-terminated C strings", "user": { "login": "jdm", "id": 27658, "avatar_url": "https://avatars1.githubusercontent.com/u/27658?v=4", "gravatar_id": "", "url": "https://api.github.com/users/jdm", "html_url": "https://github.com/jdm", "followers_url": "https://api.github.com/users/jdm/followers", "following_url": "https://api.github.com/users/jdm/following{/other_user}", "gists_url": "https://api.github.com/users/jdm/gists{/gist_id}", "starred_url": "https://api.github.com/users/jdm/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/jdm/subscriptions", "organizations_url": "https://api.github.com/users/jdm/orgs", "repos_url": "https://api.github.com/users/jdm/repos", "events_url": "https://api.github.com/users/jdm/events{/privacy}", "received_events_url": "https://api.github.com/users/jdm/received_events", "type": "User", "site_admin": false }, "labels": [ { "id": 89384911, "url": "https://api.github.com/repos/servo/servo/labels/C-assigned", "name": "C-assigned", "color": "02d7e1", "default": false }, { "id": 15997664, "url": "https://api.github.com/repos/servo/servo/labels/E-easy", "name": "E-easy", "color": "02e10c", "default": false }, { "id": 135307111, "url": "https://api.github.com/repos/servo/servo/labels/I-cleanup", "name": "I-cleanup", "color": "e11d21", "default": false } ], "state": "open", "locked": false, "assignee": null, "assignees": [ ], "milestone": null, "comments": 3, "created_at": "2018-01-31T17:04:06Z", "updated_at": "2018-01-31T22:03:56Z", "closed_at": null, "author_association": "MEMBER", "body": "When we write them by hand (eg. `b\"some string\\0\"`), we invariably get them wrong in ways that are tricky to notice (https://github.com/servo/servo/pull/19915). We should use a macro like this instead:\r\n```rust\r\nmacro_rules! c_str {\r\n ($str:expr) => {\r\n concat!($str, \"\\0\").as_bytes()\r\n }\r\n}\r\n```\r\nThis would allow us to write code like `(c_str!(\"PEParseDeclarationDeclExpected\"), Action::Skip)` instead of https://github.com/emilio/servo/blob/d82c54bd3033cc3277ebeb4854739bebe4e20f2f/ports/geckolib/error_reporter.rs#L237. We should be able to clean up all of the uses in that file.\r\n\r\nNo need to run any automated tests; if it builds with `./mach build-geckolib`, then it's good enough for a pull request.", "score": 1.0 } ]} 

My request function makes a cURL request and receives the above JSON. I then use serde_json to deserialize the JSON

main.rs

extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_json; mod engine; mod server; use engine::request; use std::string::String; use self::serde_json::{Error, Value}; #[derive(Serialize, Deserialize)] struct obj { items: Vec<String>, } fn main() { let output_jn: String = request( "https://api.github.com/search/issues?q=is:issue+label:e-easy", ).to_string(); //gets json structure as string let json: obj = serde_json::from_str(&output_jn).unwrap(); for elem in json.iter() { println!("{:?}", elem); } } 

I get the following error message

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorImpl { code: Message("invalid type: map, expected a sequence"), line: 1, column: 0 }', libcore/result.rs:945:5 note: Run with `RUST_BACKTRACE=1` for a backtrace. 

I'm certain I'm making a stupid mistake in deserializing my JSON structure, I've tried a number of permutations and combinations but I couldn't get anything to work.

4
  • let json: Vec<String> = serde_json::from_str — Why are you deserializing that JSON object as a vector? Why are you never using obj (which should be Obj)? Commented Feb 3, 2018 at 15:00
  • Thats a typo i was trying out something i saw online, I was using struct obj and getting this error. Edited the code above. Commented Feb 3, 2018 at 16:55
  • 1
    Ok, then the question becomes: Why are you deserializing that JSON array of objects as an array of strings? Commented Feb 3, 2018 at 17:11
  • I guess thats my confusion is what is the correct data structure for deserializing should it be items: Vec<Vec<String>> ? Commented Feb 4, 2018 at 3:47

1 Answer 1

35

Have a look at this part of your JSON input data:

{ ... "items": [ { ... "title": "Stop setting $CARGO_HOME to its default value", ... } ] } 
  • The top-level data structure is a JSON map, so in Rust this will be represented as a struct. I will use your name Obj.
  • The top-level JSON map has a key called "items" so in Rust this will be a field items inside the Obj struct.
  • The value of "items" in the map is a JSON array, so in Rust let's use a Vec.
  • Each element in the JSON array is a JSON map so in Rust we need a struct for those. We can call it Issue.
  • Each issue has a JSON key called "title" so this will be a field title inside the Issue struct.
  • The value of "title" is a JSON string so we can use Rust's String type for the field.

#[derive(Deserialize, Debug)] struct Obj { items: Vec<Issue>, } #[derive(Deserialize, Debug)] struct Issue { title: String, } fn main() { let j = /* get the JSON data */; let issues = serde_json::from_str::<Obj>(j).unwrap(); for i in issues.items { println!("{:#?}", i); } } 
Sign up to request clarification or add additional context in comments.

2 Comments

> The top-level data structure is a JSON map, so in Rust this will be represented as a struct < This doesn't need to be a struct - can be done with eg HashMap
@til My experiments don't bear that out, if there are keys other than "items" in the top-level (outer) dictionary/map here, i.e. keys with a value which is something other than struct Issue, which there are in the example given by the OP: "total_count": integer of some kind, and "incomplete_results": bool. I then get an Error. Whereas if everything is covered by a struct the other keys are just ignored.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.