Data serialization and deserialization

When it comes to data serialization and deserialization in Rust, there is no doubt we are talking about serde (https://crates.io/crates/serde). Serde, from serialization and deserialization, gives us a unique tool to be able to transform our data structures to JSON, TOML, XML, or any other serializable format. Let's see how it works.

We start with a simple structure:

struct MyData {
field1: String,
field2: u32,
field3: Vec<u8>,
}

And we add serde and serde_derive as dependencies to our Cargo.toml file:

[dependencies]
serde = "1.0.0"
serde_derive = "1.0.0"

Then, in our main.rs file, we just need to import the crates using extern crate and derive the Serialize trait for our structure:

extern crate serde;
#[macro_use]
extern crate serde_derive;

#[derive(Debug, Serialize)]
struct MyData {
field1: String,
field2: u32,
field3: Vec<u8>,
}

Now, we will need a frontend for our serializable structure. This is because serde by itself only gives our structure the ability to be serialized, but not the language into which it will get serialized. Let's use JSON as an example, since it's a very well-known object notation language. We first add the dependency to the Cargo.toml file:

serde_json = "1.0.0"

Then, we import it in our main.rs file and check the data serialization:

extern crate serde_json;

fn main() {
let example = MyData {
field1: "Test field".to_owned(),
field2: 33_940,
field3: vec![65, 22, 96, 43],
};

let json = serde_json::to_string_pretty(&example)
.expect("could not generate JSON string");
println!("{}", json);
}

If we execute cargo run, we will see that the output of this code is the following:

This is a perfectly formatted and prettified JSON structure. OK, so how can we convert that string back into our data structure? We need to derive Deserialize:

#[derive(Debug, Serialize, Deserialize)]
struct MyData {
field1: String,
field2: u32,
field3: Vec<u8>,
}

fn main() {
let example = MyData {
field1: "Test field".to_owned(),
field2: 33_940,
field3: vec![65, 22, 96, 43],
};

let json = serde_json::to_string_pretty(&example)
.expect("could not generate JSON string");
println!("JSON:");
println!("{}", json);

let example_back: MyData = serde_json::from_str(&json)
.expect("could not parse JSON string");
println!("Back from JSON:");
println!("{:?}", example_back);
}

This will give us this output:

This means that we can go back and forward, from JSON to a memory structure, really easily! But, of course this only works for direct structure <-> object serialization/deserialization. It won't work if any of them have different fields or names for fields. Or does it?

Well, not directly of course, but we can ask serde to modify some parameters of our structure when serializing or deserializing it. For example, since in Rust we should use snake case for our structure fields, and pascal case for enumeration and structure names, we might think it's not possible to deserialize structures with pascal case fields or enumerations with snake case variants.

Thankfully, the serde crate provides some attributes to personalize this behaviour. For example, let's suppose we want to represent the following structure in Rust:

{
"FirstData": 56,
"SecondData": "hello, world",
"ThirdData": -1.23
}

We have to first create a Rust structure that will hold this information, like this:

struct MyData {
first_data: u32,
second_data: String,
third_data: f32,
}

And then, we derive the appropriate traits. To rename the fields, we need to use the #[serde] attribute with the rename_all directive at the structure level, as you can see in the following code snippet:

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct MyData {
first_data: u32,
second_data: String,
third_data: f32,
}

fn main() {
let json = r#"{
"FirstData": 56,
"SecondData": "hello, world",
"ThirdData": -1.23
}"#;

let in_rust: MyData = serde_json::from_str(json)
.expect("JSON parsing failed");
println!("In Rust: {:?}", in_rust);

let back_to_json = serde_json::to_string_pretty(&in_rust)
.expect("Rust to JSON failed");
println!("In JSON: {}", back_to_json);
}

When you run it, you will see that the output is exactly as expected:

You can choose between "lowercase", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE", and "kebab-case". You can also rename one particular field, which is especially useful if the original structure has a reserved keyword (such as type). In this case, you can use #[serde(rename = "type")] in the field and use the name you want in your Rust structure.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.163.175