Im trying to make the following work
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let json = reqwest::get("https://www.bitmex.com/api/v1/instrument/indices")
.await?
.json::<serde_json::Value>()
.await?;
for key in json {
println!("{:?}", key["symbol"]);
}
Ok(())
}
which results in the following
--> src/main.rs:8:16
|
8 | for key in json {
| ^^^^ `Value` is not an iterator
|
= help: the trait `Iterator` is not implemented for `Value`
= note: required because of the requirements on the impl of `IntoIterator` for `Value`
I tried to implement it in the local source file serde_json-1.0.79/src/value/mod.rs
as
impl IntoIterator for Value {
type Item = Value;
type IntoIter = ValueIterator;
fn into_iter(self) -> Self::IntoIter {
ValueIterator {
slice: self.as_array().unwrap().iter(),
}
}
}
struct ValueIterator {
slice: std::slice::Iter<Value>,
}
impl Iterator for ValueIterator {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
self.slice.next()
}
}
But the error remains, what can i change in the iterator implementation to make the code work as is?
You can't. You need to wrap it a new type defined within your crate first before you implement a trait for it. The easy way around this is to make a new trait you can implement on the foreign types which does what you want. Alternatively you can wrap it in a struct
to avoid the limitations entirely.
pub trait ValueExt {
type ArrayIter: IntoIter<Item=Foo>;
fn json_array_iter(&self) -> Self::ArrayIter;
}
impl ValueExt for Value {
type ArrayIter = ValueIterator;
fn json_array_iter(&self) -> Self::ArrayIter {
ValueIterator {
slice: self.as_array().unwrap().iter(),
}
}
}
// Now you can call your trait while iterating
for item in value.json_array_iter() {
// etc.
}
That being said, the solution for serde_json
is to just expand the Value
first to check if it is an array before iterating over the elements.
if let Value::Array(elements) = value_to_iterate {
for item in elements {
// etc.
}
}
For fun, here is an iterator that will go through key value pairs of a JSON value. It will work on strings (index, UTF-8 character), arrays (index, value), and maps (key, value).
for (key, value) in json_object.items().unwrap() {
println!("Key: {:?}, Value: {:?}", key, value);
}
Here is the actual implementation and playground link.
use std::iter::Enumerate;
use std::str::Chars;
use std::slice::Iter;
use serde_json::{json, Value};
use serde_json::map::Iter as MapIter;
pub enum KVIter<'a> {
StrIter ( Enumerate<Chars<'a>>),
ArrIter (Enumerate<Iter<'a, Value>>),
MapIter (MapIter<'a>),
}
impl<'a> Iterator for KVIter<'a> {
type Item = (Value, Value);
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::StrIter(chars) => {
let (idx, character) = chars.next()?;
Some((json!(idx), json!(character)))
}
Self::ArrIter(values) => {
let (idx, value) = values.next()?;
Some((json!(idx), value.clone()))
}
Self::MapIter(items) => {
let (key, value) = items.next()?;
Some((json!(key), value.clone()))
}
}
}
}
pub trait IntoKVIter {
fn items(&self) -> Option<KVIter>;
}
impl<'a> IntoKVIter for Value {
fn items(&self) -> Option<KVIter> {
Some(match self {
Value::String(string) => KVIter::StrIter(string.chars().enumerate()),
Value::Array(values) => KVIter::ArrIter(values.into_iter().enumerate()),
Value::Object(map) => KVIter::MapIter(map.into_iter()),
_ => return None,
})
}
}