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!
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(())
}
Most helpful comment
The
serde_json::Valuetype 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), thenVec<Value>would work.Or if you know more about the array element structure, maybe
Vec<CoinPrice>. The readme discusses advantages of a strongly typed Rust representation instead ofValue.