2015-08-09 5 views
-1

Я пытаюсь разобрать JSON API, который выплевывает вывода, как это:Выбор подмножества ключей из массива JSON

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

Источник: http://api.open-notify.org/astros.json

Я использую serde для этого и есть удалось придумать следующий код до сих пор:

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

Теперь ppl_value_space получает меня это, как и ожидалось:

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

Но я хочу, чтобы добраться до ключа "name", так как, по существу, есть что-то вроде:

[{"name":"Gennady Padalka"}, {"name":"Mikhail Kornienko"}, {"name":"Scott Kelly"}, {"name":"Oleg Kononenko"}, {"name":"Kimiya Yui"}, {"name":"Kjell Lindgren"}] 

так, чтобы быть в состоянии получить только имена космонавтов в настоящее время в космосе.

Как получить "name" в пределах "people", без "craft"?

Я пытался добраться до name как так:

ppl_value_space[0].find_path(&["name"]).unwrap(); 

Но это заканчивается паника, которая в основном означает, что ключ None, так как я unwrap()Option<T>.

+0

@Shepmaster Ну, я показал код, который я пробовал разобрать этот вывод JSON, и в определенной степени преуспел, успешно проанализировав количество людей в пространстве, а также массив «people», теперь я бы хотел чтобы показать вам, как добраться до ключа «имя», но здесь я получаю панику. У меня создалось впечатление, что я показал, что я пытаюсь сделать в коде выше, (что я на самом деле написал), но теперь я отредактировал вопрос, чтобы предоставить дополнительную информацию. – CuriOne

+0

@CuriOne вы включили много кода, это правда. И то, что у вас было, выглядело хорошо. Проблема в том, что вы не показали никаких попыток получить имена из результата. Ваш предыдущий код был сродни «здесь 40 строк моего * построения» массива, как я * суммирую * все числа в массиве ». Ему не хватало решающей части, показывающей усилие на * сумме *.В любом случае, вы отредактировали его, поэтому я удалю свои комментарии через некоторое время. – Shepmaster

+0

Вы говорите, что строка кода вызывает панику, но ее невозможно даже скомпилировать: * не может индексировать значение типа '& serde_json :: value :: Value' *. Ознакомьтесь с тем, как создать [MCVE] (/ help/mcve). – Shepmaster

ответ

2

Это работает для меня:

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

Поскольку serde_json::Value является enum, это может быть много различных типов значений. И массив - это только один из них, это могут быть другие вещи, такие как строка или число. Мы ожидаем, что это будет массив, но Руста заставляет нас думать о других случаях.

В этом случае мы игнорируем все типы, за исключением Value::Array, с помощью инструкции if-let. Когда условие истинно, мы получаем ссылку на содержащийся массив.

Мы перебираем каждый элемент в массиве и находим внутри него объект имени. filter_map используется для игнорирования значений None, но вы можете сделать что-то другое.

Каждое значение распечатывается, но вы также можете указать collect в новый Vec или что-то более захватывающее.

+0

Хм, спасибо за это, я думаю, что я собираюсь поиграть с этим еще немного, но спасибо за предложение. – CuriOne

+0

Это хороший ответ и вид очевидного, причина, по которой я застрял, состоит в том, что я ожидал, что там будет способ сделать это как часть получения значений «людей» - что-то, чтобы сохранить его на одной линии , Но это отлично работает. – CuriOne

Смежные вопросы