How to iterate over JSON objects within a hierarchy?
Asked Answered
S

2

6

I want to print the name of each contact in the object deep down the hierarchy. The contact object may not have the exact same number of fields every time to make a suitable structure. How can I achieve this?

extern crate serde_json;

use serde_json::{Error, Value};
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();
}

I am very new to Rust and basically coming from a JavaScript background.

Sweetbrier answered 17/8, 2018 at 16:1 Comment(1)
To be clear, your contacts have contacts of their own? "James" is a contact of "Stefan", and "Stefan" is a contact of "John Doe"?Vying
A
2

Giving a read through the serde_json::value::Value enum I found what you were exactly looking for:

extern crate serde_json;
use serde_json::{Value};

fn main() {
    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 data: Value = serde_json::from_str(data).unwrap();
    read_value(&data, "name");
}

fn read_value(value: &Value,  search_word: &str) {
    for val in value.as_object().unwrap(){
        let (key, v) = val;
        if key == "name" {
            println!("> name: {}",v);
        }
        if v.is_object() { // if it is an object, check recursively
            read_value(&v, search_word);
        }
    }
}

It could have be done in a non recursive way but it was the simpler form I could think off. Besides I think a recursive implementation looks way more elegant and shorter code-wise for this kind of task.

Alti answered 26/12, 2021 at 2:25 Comment(0)
V
1

may not have the exact same number of fields every time to make a suitable structure

It's unclear why you think this:

extern crate serde_json; // 1.0.24
#[macro_use]
extern crate serde_derive; // 1.0.70;

use serde_json::Error;

#[derive(Debug, Deserialize)]
struct Contact {
    name: String,
    contact: Option<Box<Contact>>,
}

impl Contact {
    fn print_names(&self) {
        println!("{}", self.name);

        let mut current = self;

        while let Some(ref c) = current.contact {
            println!("{}", c.name);

            current = &c;
        }
    }
}

fn main() -> Result<(), Error> {
    let data = r#"{
      "name":"John Doe",
      "contact":{
        "name":"Stefan",
        "contact":{
          "name":"James"
        }
      }
    }"#;

    let person: Contact = serde_json::from_str(data)?;
    person.print_names();

    Ok(())
}

I've removed the extra fields from the JSON to have a smaller example but nothing changes if they are present.

See also:

Vying answered 17/8, 2018 at 17:29 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.