Skip to Content

Deserializing JSON or YAML With Rust

It’s been almost a year since I have written any Rust. I am a little rusty, to say the least, and things have changed a little. I will say that every time I come back to a functional style of programming after being away it feels more natural. I wanted to know how to deserialize a JSON or YAML file. In Go, it would be pretty easy to do it with struct tags and an if check. I stumbled across the serde crate which provided the features I wanted. That crate allows you to easily deserialize almost any format into a struct. I created a simple test JSON and YAML file to deserialize.

I created the file called data.json with the following:

{
  "firstName":"Jamie",
  "lastName": "Phillips",
  "address": {
  "street": "123 Test St",
  "city": "Testville"
  }
}

I also created a file called data.yaml with the same data, just as YAML.

firstName: Jamie
lastName: Phillips
address:
  street: 123 Test St
  city: Testville

Next, I started creating my Rust application by defining my structure in my main.rs.

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
struct Address {
    street: String,
    city: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
struct Info {
    first_name: String,
    last_name: String,
    address: Address,
}

You will notice that I added several traits to my structs. These traits allow for serialization and deserialization. I then proceeded to create my function that would take in the contents of a file and attempt to deserialize it from YAML, if the YAML failed, then it would try to deserialize it from JSON. I was about to use the ? operator to make the code a little more condensed.

fn get_data(s: &str) -> Result<Info, Box<dyn Error>> {
   return match serde_yaml::from_str::<Info>(s) {
        Ok(data) => Ok(data),
        Err(_e) => {
            let json_data = serde_json::from_str::<Info>(s)?;
            Ok(json_data)
        }
    };
}

All that was left was to create my main function to be able to compile the application and run it.

fn main() -> Result<(), Box<dyn Error>> {
    let data_string = &std::fs::read_to_string("data.json")?;
    let data = get_data(data_string)?;
    println!(
        "{} {} lives on {}, in {}",
        data.first_name, data.last_name, data.address.street, data.address.city
    );
    return Ok(());
}

If we execute it, we should see this:

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\myapp.exe`
Jamie Phillips lives on 123 Test St, in Testville

This worked perfectly for what I wanted to achieve. Rust produced a really terse application that I know will handle behave.

Thanks for reading,

Jamie

If you enjoy the content, then consider buying me a coffee.