Serde is an ubiquitous Rust crate for serialising and deserialising data. In fact, its name is a portmanteau of those two words.
I have recently been working on an RSS feed reader and digest generator, where the config is managed as a YAML file. Serde has proven to be very useful in this config management.
This post is a super basic and quick example of how to use serde and serde_yaml to easily read a YAML file and deserialise it into a type that we have instructed in Rust.
Our YAML Format
Here is an example YAML file that we want to cater for:
feeds:
- url: https://xkcd.com/rss.xml
list_length: 3
- url: https://www.youtube.com/feeds/videos.xml?channel_id=UCSp-OaMpsO8K0KkOqyBl7_w
list_length: 2
- url: https://blog.rust-lang.org/feed
list_length: 2
some_string: "this is a string"
some_object:
field_a: 123
field_b: 456
We have a list of feeds, each of which has a url and a list_length. We then have some_string (a string), followed by some_object, which has two fields field_a and field_b.
Our Dependencies
We need to install a two dependencies.
First add serde and serde_yaml to your Cargo.toml…
[dependencies]
serde = {version = "1.0", features = ["derive"]}
serde_yaml = "0.9"
…and import the following in your Rust file:
use serde::Deserialize;
use serde_yaml::Deserializer;
use std::fs;
Our Rust Data Types
We now need to create the types that represent the format of the YAML file. Our top level data structure should have fields that represent feeds, some_string, and some_object.
#[derive(Debug, Deserialize)]
struct Yaml {
feeds: Vec<Feed>,
some_string: String,
some_object: CustomObject
}
Since feeds is a list of so-called “feeds”, let’s define a feed.
#[derive(Debug, Deserialize)]
struct Feed {
url: String,
list_length: i32,
}
some_string is just a string, so we don’t need to define anything further here.
some_object is an object that has to i32 fields. Let’s define our CustomObject.
#[derive(Debug, Deserialize)]
struct CustomObject {
field_a: i32,
field_b: i32,
}
Deserialising our YAML file.
Now we can get to the fun part :)
Let us define a function which takes a filename string, and returns Result<Yaml, Box<dyn std::error::Error>>, meaning our Yaml object is returned on success, and one of Err(serde_yaml::Error) or Err(std::io::Error) is returned on failure. We will see where these two errors can be raised shortly.
fn parse_yaml(filename: String) -> Result<Yaml, Box<dyn std::error::Error>> {
Next, we want to read our file into a String. Nothing fancy here. This is where our std::io::Error could be raised.
let input = fs::read_to_string(filename)?;
Now we perform our serde magic. Simply by using the serde_yaml::from_str function, our whole YAML file can be deserialised into our Yaml Rust type. This is also where the above-mentioned serde_yaml::Error can be raised.
let yaml: Yaml = serde_yaml::from_str(&input)?;
It’s that simply!
Then we can return our Yaml object.
Ok(Yaml)
Altogether this looks like
fn parse_yaml(filename: String) -> Result<Yaml, Box<dyn std::error::Error>> {
let input = fs::read_to_string(filename)?;
let yaml: Yaml = serde_yaml::from_str(&input)?;
Ok(yaml);
}
How Does serde_yaml::from_str Work?
Looking into the implementation of serde_yaml::from_str, we can see it just calls
T::deserialize(Deserializer::from_str(s))
where T is the data type we want to deserialise into. In our case, Yaml. It has the deserialize proc macro defined because of our #[derive(Debug, Deserialize)] attribute. To create the parameter to pass into the serde macro, it uses serde_yaml::Deserializer, the serde_yaml implementation for deserialising YAML. To do this, it implements Iterator and serde::Deserializer. Read more about it here.
serde_yaml Deprecation.
Looking at the serde_yaml GitHub page, it seems like it has been archived! There is an extensive discussion about this here. I won’t be providing any guidance on what is the best path to take in this regard.
Conclusion
This post has been one of the most basic usages of serde and an accompanying crate (serde_yaml). If anything, this may provide myself with a quick reference if I forget how to use Serde in the future.