Как эффективно извлечь часть JSON как Vec без промежуточных структур?

У меня есть содержимое JSON, в котором, глубоко вложенный, есть массив чисел, который я хочу извлечь. Я бы не хотел создавать промежуточные структуры, поэтому попробовал следующее:

... get f
let json = serde_json::from_reader::<_, serde_json::Value>(f)?;
let xs: Vec<(f64, f64)> = serde_json::from_value(json["subtree"][0])?;

Это жалуется на

11 | serde_json::from_value(json["subtree"][0])?;
   |                        ^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `serde_json::value::Value`, which does not implement the `Copy` trait

Если я clone, все работает нормально:

let xs: Vec<(f64, f64)> = serde_json::from_value(json["subtree"][0].clone())?;

Но это кажется ненужным. Я не буду использовать остальную часть структуры. Как добиться этого без создания промежуточных структур и без клонирования?


person Listerone    schedule 22.08.2019    source источник


Ответы (2)


О, пропустил совершенно очевидное.

... get f
let mut json = serde_json::from_reader::<_, serde_json::Value>(f)?;
let xs: Vec<(f64, f64)> = serde_json::from_value(json["subtree"][0].take())?;
person Listerone    schedule 23.08.2019
comment
Хех, милый. Я полностью пропустил Я не буду использовать остальную часть структуры в вашем вопросе. Отметьте это как ответ, и я удалю свой. - person zrzka; 23.08.2019
comment
@zrzka Пожалуйста, оставьте свой ответ онлайн. Это полезно. - person Listerone; 24.08.2019

Я бы, вероятно, использовал Value::pointer. Пример:

use serde_json::json;

fn main() {
    let value = json!({
        "deeply": {
            "nested": {
                "array": [0, 1, 2, 3, 4, 5]
            }
        }
    });

    let numbers: Vec<u64> = value
        .pointer("/deeply/nested/array")
        .unwrap()
        .as_array()
        .unwrap()
        .iter()
        .map(|x| x.as_u64().unwrap())
        .collect();

    println!("{:?}", numbers);
}

ПРИМЕЧАНИЕ. Этот пример содержит чрезмерное использование unwrap() вызовов, что опасно и может привести к панике. Это сделано для упрощения всего примера.


Разве в этом случае вы по-прежнему не строите несколько векторов?

Нет. Давайте расширим всю эту технику.

use serde_json::{json, Value};
use std::iter::Map;
use std::slice::Iter;

fn main() {
    let value: Value = json!({
        "deeply": {
            "nested": {
                "array": [0, 1, 2, 3, 4, 5]
            }
        }
    });

    // Option<&Value> - Option & reference
    //
    //    pub enum Option<T> {
    //        None,
    //        Some(T),
    //    }
    //
    // T = &Value - reference
    let maybe_value_ref: Option<&Value> = value.pointer("/deeply/nested/array");

    // &Value - reference
    let value_ref: &Value = maybe_value_ref.unwrap();

    // Option<&Vec<Value>> - Option & reference
    //
    //    pub enum Option<T> {
    //        None,
    //        Some(T),
    //    }
    //
    // T = &Vec<Value> - reference to Vec
    let maybe_vec_ref: Option<&Vec<Value>> = value_ref.as_array();

    // &Vec<Value> - reference
    let vec_ref: &Vec<Value> = maybe_vec_ref.unwrap();

    // Iter<Value> allocation
    //
    //    pub struct Iter<'a, T: 'a> {
    //        ptr: *const T,
    //        end: *const T,
    //        _marker: marker::PhantomData<&'a T>,
    //    }
    //
    // .next() returns Option<&Value>
    let vec_ref_iter: Iter<Value> = vec_ref.iter();

    // Map<..., ...> allocation
    //
    //    pub struct Map<I, F> {
    //        iter: I,
    //        f: F,
    //    }
    //
    // .next() returns Option<u64>
    let vec_ref_iter_map: Map<Iter<Value>, fn(&Value) -> u64> =
        vec_ref_iter.map(|x: &Value| x.as_u64().unwrap());

    // Nothing happens till this point. I mean, only Iter, Map, ... structures
    // were allocated. But because they're lazy, we have to consume the last
    // Map (vec_ref_iter_map) to fire the whole machinery.
    //
    // What's going on (simplified)?
    //
    // * Vec implements FromIterator
    // * vec_ref_iter_map implements Iterator
    // * FromIterator consumes vec_ref_iter_map.next() till None (= end)
    // * vec_ref_iter_map.next() returns Option<u64>
    //   * it internally gets vec_ref_iter.next() value
    //   * if value is None then None is returned (= end)
    //   * if value is Some(x) then it applies .map() closure (x.as_u64().unwrap())
    //     and returns Some(closure result)
    //
    // The only allocated Vec here is the last one (numbers). No other Vectors
    // were allocated.
    let numbers: Vec<u64> = vec_ref_iter_map.collect();

    println!("{:?}", numbers);
}

Документация:

person zrzka    schedule 22.08.2019
comment
Разве в этом случае вы по-прежнему не строите несколько векторов? - person Listerone; 23.08.2019
comment
@Listerone см. Обновленный ответ (его конец). Результат каждого шага сохраняется во временной переменной, помеченной типом и некоторыми комментариями. Должен помочь понять, что происходит. Если нет, не стесняйтесь спрашивать. - person zrzka; 23.08.2019