Reputation: 103
I am trying to parse a JSON API that spits out output like this:
{
"message": "success",
"number": 6,
"people": [
{
"craft": "ISS",
"name": "Gennady Padalka"
},
{
"craft": "ISS",
"name": "Mikhail Kornienko"
},
{
"craft": "ISS",
"name": "Scott Kelly"
},
{
"craft": "ISS",
"name": "Oleg Kononenko"
},
{
"craft": "ISS",
"name": "Kimiya Yui"
},
{
"craft": "ISS",
"name": "Kjell Lindgren"
}
]
}
Source: http://api.open-notify.org/astros.json
I'm using serde for this and have managed to come up with the following code so far:
extern crate curl;
extern crate serde_json;
use curl::http;
use std::str;
use serde_json::{from_str};
fn main() {
// Fetch the data
let response = http::handle()
.get("http://api.open-notify.org/astros.json")
.exec().unwrap();
// Get the raw UTF-8 bytes
let raw_bytes = response.get_body();
// Convert them to a &str
let string_body: &str = str::from_utf8(&raw_bytes).unwrap();
// Get the JSON into a 'Value' Rust type
let json: serde_json::Value = serde_json::from_str(&string_body).unwrap();
// Get the number of people in space
let num_of_ppl: i64 = json.find_path(&["number"]).unwrap().as_i64().unwrap();
println!("There are {} people on the ISS at the moment, they are: ", num_of_ppl);
// Get the astronauts
// Returns a 'Value' vector of people
let ppl_value_space = json.find_path(&["people"]).unwrap();
println!("{:?}", ppl_value_space);
}
Now, ppl_value_space
gets me this, as expected:
[{"craft":"ISS","name":"Gennady Padalka"}, {"craft":"ISS","name":"Mikhail Kornienko"}, {"craft":"ISS","name":"Scott Kelly"}, {"craft":"ISS","name":"Oleg Kononenko"}, {"craft":"ISS","name":"Kimiya Yui"}, {"craft":"ISS","name":"Kjell Lindgren"}]
But, I want to get to the "name"
key, as to essentially have something like:
[{"name":"Gennady Padalka"}, {"name":"Mikhail Kornienko"}, {"name":"Scott Kelly"}, {"name":"Oleg Kononenko"}, {"name":"Kimiya Yui"}, {"name":"Kjell Lindgren"}]
So as to be able to get only the names of the astronauts currently in space.
How do I get the "name"
within "people"
, without the "craft"
?
I tried to get to name
like so:
ppl_value_space[0].find_path(&["name"]).unwrap();
But it ends with a panic, which basically means that the key is None
, since I unwrap()
an Option<T>
.
Upvotes: 0
Views: 1981
Reputation: 430634
This worked for me:
if let &Value::Array(ref people) = ppl_value_space {
let names = people.iter().filter_map(|person| person.find_path(&["name"]));
for name in names {
println!("{:?}", name);
}
}
Since a serde_json::Value
is an enum
, it can be many different types of values. And array is just one of those, it could be other things like a string or a number. We expect it to be an array, but Rust forces us to think about the other cases.
In this case, we ignore all types except for a Value::Array
by using an if-let statement. When the condition is true we get a reference to the contained array.
We iterate over each item in the array and find the name object inside of it. filter_map
is used to ignore None
values, but you may want to do something different.
Each value is printed out, but you could also collect
them into a new Vec
or something more exciting.
Upvotes: 2