Read arbitrary JSON without much preparation

serde_json from_reader Value JSON as_str unwrap as_f64 as_u64 as_array assert_eq!

In order to read a JSON file, probably the best approach is to define a struct that will hold the content of the JSON file. Unfortunately it can be time consuming, so to get started you might want to read in the content of the JSON file and use the hand-picked values from the data.

In order to do this we'll use serde_json as you can see in the Cargo.toml file:

examples/read-arbitrary-json/Cargo.toml

[package]
name = "read-arbitrary-json"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde_json = "1.0"

This is the data file we use. It does not have any real meaning, it just contains all kinds of data types.

examples/read-arbitrary-json/data.json

{
    "fname": "Foo",
    "lname": "Bar",
    "year": 1992,
    "height": 178.2,
    "married": true,
    "numbers": [23, 19, 42],
    "children": [
        {
            "name": "Alpha",
            "birthdate": 2020
        },
        {
            "name": "Beta",
            "birthdate": 2022
        }
    ]
}

The code:

examples/read-arbitrary-json/src/main.rs

use std::fs::File;

fn main() {
    let filename = get_filename();

    let data = match File::open(&filename) {
        Ok(file) => {
            let data: serde_json::Value =
                serde_json::from_reader(&file).expect("JSON parsing error");
            data
        }
        Err(error) => {
            eprintln!("Error opening file {}: {}", filename, error);
            std::process::exit(1);
        }
    };
    dbg!(&data);
    assert_eq!(data.get("fname").unwrap().as_str().unwrap(), "Foo");

    assert_eq!(data["lname"].as_str().unwrap(), "Bar");
    assert_eq!(data["height"].as_f64().unwrap(), 178.2);
    assert_eq!(data["year"].as_u64().unwrap(), 1992);
    assert_eq!(data["numbers"].as_array().unwrap().len(), 3);
    assert_eq!(data["numbers"][0].as_u64().unwrap(), 23);
    assert_eq!(data["children"].as_array().unwrap().len(), 2);
    assert_eq!(data["children"][0]["name"].as_str().unwrap(), "Alpha");
}

fn get_filename() -> String {
    let args: Vec<String> = std::env::args().collect();
    if args.len() != 2 {
        eprintln!("Usage: {} FILENAME", args[0]);
        std::process::exit(1);
    }
    args[1].to_string()
}

The program expects the name of the JSON file on the command line. We have a function called get_filename that will return the name of the file or will exit if the user did not provide a filename.

Then we open the JSON file and read in the content and convert it into a data-structure of type Value using the serde_json::from_reader function. We assigned it to a variable called data. When you work on real data, please try using a more descriptive name!

Then we see two ways to access the data one is calling the get method on the data. This will return an Option so we need to unwrap it to get the real value or we need to arrange some more serious error handling if we are not sure the field "fname" exists.

data.get("fname").unwrap()

Alternatively we can access the data using square brackets:

data["lname"]

In either case we get another Value from which we can get the real value by using one of the as_ functions listed for the Value.

In normal code you'd probably do something with the values these functions return, but for our demonstration I used the assert_eq! macro to compare the values to expected values.

Running the code

cargo run data.json

will produce this output:

[src/main.rs:17] &data = Object {
    "children": Array [
        Object {
            "birthdate": Number(2020),
            "name": String("Alpha"),
        },
        Object {
            "birthdate": Number(2022),
            "name": String("Beta"),
        },
    ],
    "fname": String("Foo"),
    "height": Number(178.2),
    "lname": String("Bar"),
    "married": Bool(true),
    "numbers": Array [
        Number(23),
        Number(19),
        Number(42),
    ],
    "year": Number(1992),
}

Conclusion

This way of reading a JSON file can be useful to get started, but we'll need a more robust way to verify the data and to make it easier to use the data once we read it in.

Related Pages

Read simple JSON and deserialize into a struct
Read simple JSON and deserialize into a struct
JSON and Rust

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo