Json: Having issues iterating through an serde_json::Value

Created on 3 Mar 2018  Â·  13Comments  Â·  Source: serde-rs/json

Sorry to post this on the issues forum, but I cannot find a clean way to do this. I am new to rust and would like to do iterate through this data structure (when printed withprintln!("{:?}\n\n",v);)

Array([Object({"24h_volume_usd": String("7555680000.0"), "available_supply": String("16896337.0"), "id": String("bitcoin"), "last_updated": String("1520039066"), "market_cap_usd": String("187946404620"), "max_supply": String("21000000.0"), "name": String("Bitcoin"), "percent_change_1h": String("0.46"), "percent_change_24h": String("0.45"), "percent_change_7d": String("7.4"), "price_btc": String("1.0"), "price_usd": String("11123.5"), "rank": String("1"), "symbol": String("BTC"), "total_supply": String("16896337.0")}), Object({"24h_volume_usd": String("1859040000.0"), "available_supply": String("97947252.0"), "id": String("ethereum"), "last_updated": String("1520039051"), "market_cap_usd": String("84237477378.0"), "max_supply": Null, "name": String("Ethereum"), "percent_change_1h": String("0.3"), "percent_change_24h": String("-1.49"), "percent_change_7d": String("-0.41"), "price_btc": String("0.0775658"), "price_usd": String("860.029"), "rank": String("2"), "symbol": String("ETH"), "total_supply": String("97947252.0")}), Object({"24h_volume_usd": String("276913000.0"), "available_supply": String("39091956706.0"), "id": String("ripple"), "last_updated": String("1520039041"), "market_cap_usd": String("36054628946.0"), "max_supply": String("100000000000"), "name": String("Ripple"), "percent_change_1h": String("1.39"), "percent_change_24h": String("-0.6"), "percent_change_7d": String("-7.81"), "price_btc": String("0.00008318"), "price_usd": String("0.922303"), "rank": String("3"), "symbol": String("XRP"), "total_supply": String("99992520283.0")}), Object({"24h_volume_usd": String("417415000.0"), "available_supply": String("16996550.0"), "id": String("bitcoin-cash"), "last_updated": String("1520039053"), "market_cap_usd": String("21658363734.0"), "max_supply": String("21000000.0"), "name": String("Bitcoin Cash"), "percent_change_1h": String("-0.15"), "percent_change_24h": String("-1.56"), "percent_change_7d": String("-0.24"), "price_btc": String("0.114927"), "price_usd": String("1274.28"), "rank": String("4"), "symbol": String("BCH"), "total_supply": String("16996550.0")})])

extern crate term_painter;
extern crate reqwest;


extern crate serde_json;
// JSON Parsing and Construction
// https://github.com/serde-rs/json
use serde_json::{Value};

mod getcointicker;

//use std::io;

//https://lukaskalbertodt.github.io/term-painter/term_painter/
use term_painter::ToStyle;
use term_painter::Color::*;
//use term_painter::Attr::*;
use getcointicker::coinprices;

fn main() {
    println!("rusty{}, ",
        Yellow.paint("Horde"),
    );
    let cp = match coinprices(4) {
        Result::Ok(val) => {val},
        Result::Err(err) => {format!("Unable to get coin prices: {}",err)}
    };
    //println!("{}",cp);  
    let v: Value = match serde_json::from_str(&cp){
        Result::Ok(val) => {val},
        Result::Err(err) => {panic!("Unable to parse json: {}",err)}
    };

    //what is the proper way to iterate through all the values in the array
    for i in &v.into_iter() {
        println!("{:?}\n\n",v[i]);
    }
    println!("{:?}\n\n",v[0]); //this works no problem

    println!("{}",v[0]["market_cap_usd"]);  
}

I have tried several ways...the latest was the following:

error[E0599]: no method named `into_iter` found for type `serde_json::Value` in the current scope
  --> src/main.rs:34:16
   |
34 |     for i in v.into_iter() {
   |                ^^^^^^^^^
   |
   = note: the method `into_iter` exists but the following trait bounds were not satisfied:
           `serde_json::Value : std::iter::IntoIterator`
           `&serde_json::Value : std::iter::IntoIterator`
           `&mut serde_json::Value : std::iter::IntoIterator`

error: aborting due to previous error

error: Could not compile `rustyhorde`.

Thank you so much for your help!

support

Most helpful comment

The serde_json::Value type is only intended for when you know nothing about what structure the JSON data has. If you know more than nothing about the JSON data, you will typically be better off using a more specific type. In this case if you know the data is an array (but nothing else about it), then Vec<Value> would work.

let v: Vec<Value> = serde_json::from_str(&cp)?;
for item in &v {
    println!("{:?}\n", item);
}

Or if you know more about the array element structure, maybe Vec<CoinPrice>. The readme discusses advantages of a strongly typed Rust representation instead of Value.

#[derive(Deserialize, Debug)]
struct CoinPrice {
    #[serde(rename = "24h_volume_usd")]
    volume_usd: String,
    available_supply: String,
    id: String,
    last_updated: String,
    market_cap_usd: String,
    max_supply: Option<String>,
    name: String,
    percent_change_1h: String,
    percent_change_24h: String,
    percent_change_7d: String,
    price_btc: String,
    price_usd: String,
    rank: String,
    symbol: String,
    total_supply: String,
}

All 13 comments

The serde_json::Value type is only intended for when you know nothing about what structure the JSON data has. If you know more than nothing about the JSON data, you will typically be better off using a more specific type. In this case if you know the data is an array (but nothing else about it), then Vec<Value> would work.

let v: Vec<Value> = serde_json::from_str(&cp)?;
for item in &v {
    println!("{:?}\n", item);
}

Or if you know more about the array element structure, maybe Vec<CoinPrice>. The readme discusses advantages of a strongly typed Rust representation instead of Value.

#[derive(Deserialize, Debug)]
struct CoinPrice {
    #[serde(rename = "24h_volume_usd")]
    volume_usd: String,
    available_supply: String,
    id: String,
    last_updated: String,
    market_cap_usd: String,
    max_supply: Option<String>,
    name: String,
    percent_change_1h: String,
    percent_change_24h: String,
    percent_change_7d: String,
    price_btc: String,
    price_usd: String,
    rank: String,
    symbol: String,
    total_supply: String,
}

Thank you very much for the quick reply, was struggling with this for a while. I will try these approaches. In the structure is it possible to flag a value to be ignored (similar to the rename?)

Any fields not listed in the struct are ignored.

Thanks! Great library

On Sat, Mar 3, 2018, 12:02 PM David Tolnay notifications@github.com wrote:

Any fields not listed in the struct are ignored.

—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
https://github.com/serde-rs/json/issues/417#issuecomment-370163028, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAOJnLZgxGNtWMvv9xEAqCIFZ47lm6jRks5taszDgaJpZM4SatND
.

What if I know my JSON data is a mapping from strings to strings, e.g.

{
  "foo": "bar",
  "baz": "qux"
}

but I don't know "foo" will be named "foo"? (Basically what I do know is I have a mapping of strings to strings, but that mapping will vary.) Is there a better way of specifying a JSON value with that kind of structure?

What if I know my JSON data is a mapping from strings to strings

You can deserialize it to HashMap<String, String>.

Ah to answer my own question, it seems something like this will work:

let m: HashMap<String, String> = serde_json::from_str(&content)?;

@dtolnay I still don't know how to iterate the Value directly. if I have a HashMap<String, Anything>, how can I iterate it?

@xphoniex Try

use std::collections::HashMap;
type JsonMap = HashMap<String, serde_json::Value>;

fn main() -> Result<(), serde_json::Error> {
    let input = r#"
        {
            "a": "a",
            "b": {"c": "c", "d": "d"},
            "e": 5
        }"#;
    let map: JsonMap = serde_json::from_str(input)?;
    for (key, value) in map.iter() {
        match key.as_str() {
            "a" => assert!(value.is_string()),
            "b" => assert!(value.is_object()),
            "e" => assert!(value.is_number()),
            _ => (),
        }
    };
    Ok(())
}

is there a similar way to do this using serde_json::from_value() instead of from str?
e.g.

let v: Value = serde_json::from_str(&cp)?;
let sub_value: Vec<Value> = serde_json::from_value(&v["sub_value"])?;
for item in &sub_value {
    println!("{:?}\n", item);
}

The error of that code is related to a lack of a copy trait

error[E0507]: cannot move out of index of ``serde_json::Value`
move occurs because value has type `serde_json::Value`, which does not implement the `Copy` trait

The workaround I have is from_string(to_string()) but that seems pretty inefficient.

let v: Value = serde_json::from_str(&cp)?;
let sub_value: Vec<Value> = serde_json::from_str(&v["sub_value"].to_string())?;
for item in &sub_value {
    println!("{:?}\n", item);
}

Hi @jeff-hykin If you are sure the top-level value is a vector then you can do something similar to the HashMap case:

use serde_json::{Value, Error, from_str}; // 1.0.64

fn main() -> Result<(), Error> {
    let input = r#"
        [
            "a",
            {"c": "c", "d": "d"},
            ["e", 5]
        ]"#;
    let sub_values: Vec<Value> = from_str(input)?;
    for item in sub_values.iter() {
        // each item is a borrow of the sub_value
        println!("{:?}", item);
    }
    Ok(())
}

See this working code in the Rust playground.

@brotskydotcom That makes sense, and thanks for the quick reply (and version info and playground!).
But I'm mostly trying to work on the best way to extract/iterate when its not top-level (e.g. nested arrays)

use serde_json::{Value, Error, from_str}; // 1.0.64

fn main() -> Result<(), Error> {
    let input = r#"
        {
             "name": "thing",
             "nested_array": [
                 "a",
                 {"c": "c", "d": "d"},
                 ["e", 5]
             ]
        }"#;

    let parsed_data: Value = from_str(input)?;
    let nested_array: Vec<Value> = parsed_data["nested_array"];              // <- note: doesnt work 
    for item in nested_array.iter() {
        // each item is a borrow of the sub_value
        println!("{:?}", item);
    }

    Ok(())
}

Finally got it after digging around on here 🎊

#![allow(unused)]
use serde_json::{Value, Error, from_str}; // 1.0.64

fn main() -> Result<(), Error> {
    let input = r#"
    {
        "name": "bob",
        "vals": [
            "a",
            {"c": "c", "d": "d"},
            ["e", 5]
        ]
    }"#;
    let parsed_data: Value = from_str(input)?;
    let an_array = parsed_data["vals"].as_array().unwrap();
    for item in an_array.iter() {
        // each item is a borrow of the sub_value
        println!("{:?}", item);
    }
    Ok(())
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

VictorKoenders picture VictorKoenders  Â·  6Comments

jaredforth picture jaredforth  Â·  4Comments

NoraCodes picture NoraCodes  Â·  6Comments

vityafx picture vityafx  Â·  6Comments

dtolnay picture dtolnay  Â·  6Comments