How to Iterate Over JSON objects with hierarchy?

Hi,
I am very new to Rust and basically from JavaScript background.

I have a problem below where i want to print "name" of each contact in the object deep down the hierarchy.
The contact object as in JavaScript may not be having exact same number of fields every time to make a suitable Structure.

Could you please help how to achieve this in Rust.

Thanks in Advance!

extern crate serde_json;

use serde_json::{Value, Error};
use std::collections::HashMap;

fn untyped_example() -> Result<(), Error> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"{
                    "name": "John Doe",
                    "age": 43,
                    "phones": [
                      "+44 1234567",
                      "+44 2345678"
                    ],
                    "contact": {
                           "name": "Stefan",
                            "age": 23,
                             "optionalfield": "dummy field",
                            "phones": [
                              "12123",
                              "345346"
                            ],
                            "contact": {
                                   "name": "James",
                                    "age": 34,
                                    "phones": [
                                      "23425",
                                      "98734"
                                    ]
                            }
                    }
                  }"#;

    let mut d:HashMap<String, Value> = serde_json::from_str(&data)?;
    for (str, val) in d {
        println!("{}", str);
        if str == "contact" {
            d = serde_json::from_value(val)?;
        }
    }

    Ok(())
}

fn main() {
    untyped_example().unwrap();
}

By default, serde ignores keys not in the structure, and handles sometimes present keys using Option. So this will work out of the box with a structure like this:

#[derive(Deserialize)]
struct Person {
    name: String,
    age: u32,
    // add more fields you want here, wrap optional ones in Option<>, for example:
    //phones: Vec<String>,
    //optionalfield: Option<String>,
    // the recursive case needs Box to avoid infinite struct sizes
    contact: Option<Box<Person>>,
}

// then:
let person: Person = serde_json::from_str(&data)?;
2 Likes

This solution works really well when:

  • you know enough about the incoming data to be sure that it has this shape (so parsing won't fail), and
  • you need only some parts of the data (so it's fine to ignore the rest).

If those things aren't both true, serde still has some other useful tricks for you. In reverse order to above:

  • if you might want some of the other fields, but you're not sure what might be present enough to give them a concrete Option<Type>, you can have serde slurp them into a key-value hash using the flatten attribute: Struct flattening · Serde.
  • if you're not sure even of the shape of the data, and want to parse any (syntactically valid) JSON in order to go exploring and querying through the data, you can pull it into a more generic form: serde_json - Rust. The Value type here is the same as used in the key-value hash above.

The latter case is as close as it's possible to come to arbitrary JSON ingestion in a more dynamic language like JavaScript or Perl. The type structure encodes all the same information about content (this key held a JSON array or string or..) as you'd get by introspection of the native data tree there.

On the other hand if you know more about the shape of the data above, there's another useful thing. Instead of options, if there are a few variants of a contact with some specific differences, you can parse them into an enum of structs representing each variant: Enum representations · Serde