How do I parse a JSON File?
Asked Answered
S

6

106

I have this so far in my goal to Parse this JSON data in Rust:

extern crate rustc_serialize;
use rustc_serialize::json::Json;
use std::fs::File;
use std::io::copy;
use std::io::stdout;

fn main() {
    let mut file = File::open("text.json").unwrap();
    let mut stdout = stdout();
    let mut str = &copy(&mut file, &mut stdout).unwrap().to_string();
    let data = Json::from_str(str).unwrap();
}

and text.json is

{
    "FirstName": "John",
    "LastName": "Doe",
    "Age": 43,
    "Address": {
        "Street": "Downing Street 10",
        "City": "London",
        "Country": "Great Britain"
    },
    "PhoneNumbers": [
        "+44 1234567",
        "+44 2345678"
    ]
}

What should be my next step into parsing it? My primary goal is to get JSON data like this, and parse a key from it, like Age.

Shores answered 17/5, 2015 at 22:20 Comment(2)
It looks like you read the right page for parsing it. Did you see the example down the page that looks like exactly what you want?Ignescent
@Ignescent Yeah I added let obj = data.as_object().unwrap(); and got thread '<main>' panicked at 'called Option::unwrap()` on a None value', C:/bo t/slave/stable-dist-rustc-win-32/build/src/libcore\option.rs:362 }An unknown error occurred`Shores
B
96

Serde is the preferred JSON serialization provider. You can read the JSON text from a file a number of ways. Once you have it as a string, use serde_json::from_str:

fn main() {
    let the_file = r#"{
        "FirstName": "John",
        "LastName": "Doe",
        "Age": 43,
        "Address": {
            "Street": "Downing Street 10",
            "City": "London",
            "Country": "Great Britain"
        },
        "PhoneNumbers": [
            "+44 1234567",
            "+44 2345678"
        ]
    }"#;

    let json: serde_json::Value =
        serde_json::from_str(the_file).expect("JSON was not well-formatted");
}

Cargo.toml:

[dependencies]
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.48"

You could even use something like serde_json::from_reader to read directly from an opened File.

Serde can be used for formats other than JSON and it can serialize and deserialize to a custom struct instead of an arbitrary collection:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Person {
    first_name: String,
    last_name: String,
    age: u8,
    address: Address,
    phone_numbers: Vec<String>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Address {
    street: String,
    city: String,
    country: String,
}

fn main() {
    let the_file = /* ... */;

    let person: Person = serde_json::from_str(the_file).expect("JSON was not well-formatted");
    println!("{:?}", person)
}

Check the Serde website for more details.

Brann answered 20/4, 2018 at 21:53 Comment(0)
S
43

Solved by the many helpful members of the Rust community:

extern crate rustc_serialize;
use rustc_serialize::json::Json;
use std::fs::File;
use std::io::Read;

fn main() {
    let mut file = File::open("text.json").unwrap();
    let mut data = String::new();
    file.read_to_string(&mut data).unwrap();

    let json = Json::from_str(&data).unwrap();
    println!("{}", json.find_path(&["Address", "Street"]).unwrap());
}
Shores answered 17/5, 2015 at 23:13 Comment(1)
Note that the rustc_serialize repo says it is deprecated now in favor of github.com/serde-rs/jsonDunlin
T
43

There is a brief and complete example of how to read JSON from file in serde_json::de::from_reader docs.

Here is a short snippet for:

  • reading a file
  • parsing its contents as a JSON
  • and extracting a field with the desired key

Enjoy:

let file = fs::File::open("text.json")
    .expect("file should open read only");
let json: serde_json::Value = serde_json::from_reader(file)
    .expect("file should be proper JSON");
let first_name = json.get("FirstName")
    .expect("file should have FirstName key");
Trailblazer answered 24/10, 2018 at 8:49 Comment(3)
An answer using serde_json is already present. Please edit this answer to more clearly show what is different and useful that warrants repeating.Brann
Thanks, I like the approach without the need to type everything in the JSONSquire
Using from_reader on a file is really slow for large JSON files, while reading the file first to a string (as in some of the other answers) is much faster. According to this GitHub comment, the from_reader approach reads the file 1 byte at a time, severely limiting performance.Omidyar
J
8

Upvoted the accepted answer (as it helps), but just adding my answer, using the widely used serde_json crate referenced by @FrickeFresh

Assuming your foo.json is

{
    "name": "Jane",
    "age": 11
}

Implementation would look something like

extern crate serde;
extern crate json_serde;
#[macro_use] extern crate json_derive;
use std::fs::File;
use std::io::Read;

#[derive(Serialize, Deserialize)]
struct Foo {
    name: String,
    age: u32,
}

fn main() {
   let mut file = File::open("foo.json").unwrap();
   let mut buff = String::new();
   file.read_to_string(&mut buff).unwrap();

   let foo: Foo = serde_json::from_str(&buff).unwrap();
   println!("Name: {}", foo.name);
}
Joslyn answered 16/8, 2018 at 2:4 Comment(4)
An answer using serde_json is already present. Please edit this answer to more clearly show what is different and useful that warrants repeating.Brann
Unlike in other answer how to get the string from the file was not described. This is a full example of how to use serde to read from a separate file (foo.json) and take that string read_to_string and finally unwrap it. Also, the links to serde_json did not work nor did the example for serde_json::from_reader() work from the website.Wingspread
It doesn't work. buff does not live long enough borrowed value does not live long enoughVolscian
Thanks for this, the other answer above this skipped over how to actually open the file for some reason.Lobar
O
6

You can extract this functionality into a utility. As per their docs, this might be a valid piece of software

use std::{
    fs::File,
    io::BufReader,
    path::Path,
    error::Error
};

use serde_json::Value;

fn read_payload_from_file<P: AsRef<Path>>(path: P) -> Result<Value, Box<dyn Error>> {
    // Open file in RO mode with buffer
    let file = File::open(path)?;
    let reader = BufReader::new(file);

    // Read the JSON contents of the file
    let u = serde_json::from_reader(reader)?;

    Ok(u)
}

fn main() {
  let payload: Value = 
     read_payload_from_file("./config/payload.json").unwrap();
}
Oversleep answered 1/8, 2022 at 16:12 Comment(0)
N
-3

Rust comes with an elegant native-json crate, which declares native JSON object with Rust, and native acccess to members.

Example of native-json

use native_json::json;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};

fn main()
{
    let mut json = json!{
        name: "native json",
        style: {
            color: "red",
            size: 12,
            bold: true,
            range: null
        },
        array: [5,4,3,2,1],
        vector: vec![1,2,3,4,5],
        hashmap: HashMap::from([ ("a", 1), ("b", 2), ("c", 3) ]);,
        students: [
            {name: "John", age: 18},
            {name: "Jack", age: 21},
        ],
    };

    // Native access
    json.style.size += 1;
    json.students[0].age += 2;

    // Debug
    println!("{:#?}", t);

    // Stringify
    let text = serde_json::to_string_pretty(&json).unwrap();
    println!("{}", text);
}

native-json way

use wsd::json::*;

fn main() {
    // Declare as native JSON object
    let mut john = json!{
        name: "John Doe",
        age: 43,
        phones: [
            "+44 1234567",
            "+44 2345678"
        ]
    };

    // Native access to member
    john.age += 1;

    println!("first phone number: {}", john.phones[0]);

    // Convert to a string of JSON and print it out
    println!("{}", stringify(&john, 4));
}

serde_json way

use serde_json::json;

fn main() {
    // The type of `john` is `serde_json::Value`
    let john = json!({
        "name": "John Doe",
        "age": 43,
        "phones": [
            "+44 1234567",
            "+44 2345678"
        ]
    });

    println!("first phone number: {}", john["phones"][0]);

    // Convert to a string of JSON and print it out
    println!("{}", john.to_string());
}
Nuncle answered 10/11, 2022 at 9:49 Comment(1)
Nice but it does not answer the question. This allows you to paste json into your code but not to parse an external file.Simoniac

© 2022 - 2024 — McMap. All rights reserved.