Rust - Serde Json By Example
Tags: rust, serde, json, tutorial
Published: 2021-01-03 17:03:23

## Basic struct serialization and deserialization

Serialization happens via [serde_json::to_string](https://docs.serde.rs/serde_json/ser/fn.to_string.html).
`serde_json::to_string_pretty` can be used for enhancing the readibility if you're printing the data.

```rust
pub fn to_string<T: ?Sized>(value: &T) -> Result<String> 
where
    T: Serialize
```

Deserialization happens via [serde_json::from_str](https://docs.serde.rs/serde_json/fn.from_str.html)
```rust
pub fn from_str<'a, T>(s: &'a str) -> Result<T> 
where
    T: Deserialize<'a>
```

Since, the return type for both is a `Result<T>`, the examples below end up `unwrap()`ing the values.

#### Unit structs
```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct U;

fn main() {
    let u = U;

    // serialization
    let s = serde_json::to_string(&u).unwrap();
    // `s` represented as `null`
    println!("{}", s);

    // deserialization
    let d: U = serde_json::from_str("null").unwrap();
    // `d` is U
    println!("{:?}", d);
}
// Output:
// null
// U
```

#### Tuple structs
```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct T(u8, f64, bool, String);

fn main() {
    // serialization
    let t = T(10u8, 3.14159, true, "Hello".to_owned());
    let s = serde_json::to_string(&t).unwrap();
    // `s` represented as `[10, 3.14159, true, "Hello"]`
    println!("{}", s);
    
    // deserialization
    let d: T = serde_json::from_str(r#"[10, 3.14159, true, "Hello"]"#).unwrap();
    // `d` is `T(10u8, 3.14159, true, "Hello".to_owned())`;
    println!("{:?}", d);
}
// Output:
// [10,3.14159,true,"Hello"]
// T(10, 3.14159, true, "Hello")

```

#### Haskell Newtype-like structs
```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct N(i32);

fn main() {
    // serialization
    let n = N(10i32);
    let s = serde_json::to_string(&n).unwrap();
    // `s` represented as `10`
    println!("{}", s);

    // deserialization
    let d: N = serde_json::from_str("10").unwrap();
    // `d` is `N(10)`;
    println!("{:?}", d);
}
// Output:
// 10
// N(10)

```


#### C-like structs
```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct C {
    a: i32,
    b: f64,
    c: bool,
    d: String,
}

fn main() {
    // serialization
    let t = C {
        a: 10,
        b: 3.14159,
        c: true,
        d: "Hello".to_owned(),
    };
    let s = serde_json::to_string(&t).unwrap();
    // `s` represented as `{"a": 10, "b": 3.14159, "c": true, "d": "Hello"}`
    println!("{}", s);

    // deserialization
    let d: C = serde_json::from_str(&s).unwrap();
    println!("{:?}", d);
}

// Output:
// {"a":10,"b":3.14159,"c":true,"d":"Hello"}
// C { a: 10, b: 3.14159, c: true, d: "Hello" }

```

#### Optional values
The examples you have come across so far had structs with *required values*.
`null` is a valid json value that is often used in cases data is missing.
Take for example the following:
```rust
#[derive(Serialize, Deserialize)]
struct C {
    a: i32,
    b: f64,
}

// serialization with missing value
let t = C {
  b: 3.14159,
};
serde_json::to_string(&t).unwrap();
```
The above program would fail to compile since we've missed `a` entirely
```
error[E0063]: missing field `a` in initializer of `C`
```

Deserialization with a null value for `a` would cause errors.
Its type is i32 and we can't arbitrarily choose `0` to indicate absence of data. 
In such cases, it's preferred that we mark the values as *Optional* as shown below.

```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct C {
    a: Option<i32>,
    b: f64,
}

fn main() {
    // serialization with missing value
    let t = C {
        a: None,
        b: 3.14159,
    };
    println!("{}", serde_json::to_string(&t).unwrap());
    // `{"a": null, "b": 3.14159}`

    // deserialization with missing value
    let d: C = serde_json::from_str(r#"{"a": null, "b": 3.14159}"#).unwrap();
    // `d` equals `C { a: None, b: 3.14159 }`
    println!("{:?}", d);
    
    println!("Hello, world!");
}

// Output:
// {"a":null,"b":3.14159}
// C { a: None, b: 3.14159 }
// Hello, world!

```

#### Default values

```rust
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Test {
    // Use default_str_fun() as the default if "my_str" is missing
    #[serde(default = "default_str_fun")]
    my_str: String,

    // Use MyType::default if "my_type" is missing
    #[serde(default)]
    my_type: MyType,
}

fn default_str_fun() -> String {
    "hi".to_owned()
}

/// Timeout in seconds.
#[derive(Deserialize, Debug)]
struct MyType(Vec<i32>);
impl std::default::Default for MyType {
    fn default() -> Self {
        Self(vec![1, 2, 3])
    }
}

fn main() {
    let d1: Test = serde_json::from_str(r#"{"my_type": [1]}"#).unwrap();
    // `d1` equals `Test { my_str: "hi".to_owned(), my_type: MyType(vec![1]) }`
    println!("{:?}", d1);

    let d2: Test = serde_json::from_str(r#"{"my_str": "hello world"}"#).unwrap();
    // `d2` equals `Test { my_str: "hello world".to_owned(), my_type: MyType(vec![1,2,3]) }`
    println!("{:?}", d2);
    
    println!("Hello, world!");
}

// Output:
// Test { my_str: "hi", my_type: MyType([1]) }
// Test { my_str: "hello world", my_type: MyType([1, 2, 3]) }
// Hello, world!

```

#### Missing field vs null value
A `null` value isn't the same as a missing field altogether. 
The following program panics on deserialization.

```rust
// ================
// BROKEN
// ================
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Test {
    // Use default_str_fun() as the default if "my_str" is missing
    #[serde(default = "default_str_fun")]
    my_str: String,

    // Use MyType::default if "my_type" is missing
    #[serde(default)]
    my_type: MyType,
}

fn default_str_fun() -> String {
    "hi".to_owned()
}

/// Timeout in seconds.
#[derive(Deserialize, Debug)]
struct MyType(Vec<i32>);
impl std::default::Default for MyType {
    fn default() -> Self {
        Self(vec![1, 2, 3])
    }
}

fn main() {
 
    let d: Test = serde_json::from_str(r#"{
        "my_str": "hello world", 
        "my_type": null
    }"#).unwrap();
    println!("{:?}", d);
}

// Output:
// Running `target/debug/playground`
// thread 'main' panicked at 'called `Result::unwrap()` on an 
// `Err` value: Error("invalid type: null, expected a sequence", line: 3, column: 23)', 
// src/main.rs:32:10
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

```


## json! macro
The `json!` macro allows one to construct `serde_json::Value` objects by hand.

```rust
let j: serde_json::Value = json!({
    "a": 10u8,
    "b": 3.14159,
    "c": true,
    "d": "Hello".to_owned(),
});
let s = j.to_string();
// `s` represented as `{"a": 10, "b": 3.14159, "c": true, "d": "Hello"}`
```

## Deserializing to Map
```rust
// deserializing to Map<String, Value>
use serde_json::{Value, Map};
println!(
    "{:?}", 
    serde_json::from_str::<Map<String, Value>>(r#"
        {"gender":"male","blah":25}
        "#).unwrap());
// {"blah": Number(25), "gender": String("male")}
```

## enums

The default enum representation has the enum variant 'externally tagging' the content.
```rust
#[derive(Serialize, Deserialize)]
enum Color {
    C { red: u8, green: u8, blue: u8 },
    T(u8, u8, u8),
    N(i32),
    D,
}
let a = Color::C { 
    red: 10,
    green: 100,
    blue: 125
}; 
// `a` represented as `{"C":{"red":10,"green":100, "blue": 125}}`

let b = Color::T(10, 100, 125); 
// `b` represented as `{"T":[10,100,125]}`

let c = Color::N(0);
// `c` represented as `{"N":0}`

let d = Color::D;
// `d` represented as `"D"`
```

It's also possible to delineate the tag and content separately in the object
next to each other

```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "body")]
enum Color {
    C { red: u8, green: u8, blue: u8 },
    T(u8, u8, u8),
    N(i32),
    D,
}

fn main() {
    let a = Color::C {
        red: 10,
        green: 100,
        blue: 125,
    };
    println!("{}", serde_json::to_string(&a).unwrap());
    // {"type":"C","body":{"red":10,"green":100,"blue":125}}
}

```

Ommitting the `content` should merge the tag with the rest of the content.
```rust
// ===============
// BROKEN PROGRAM
// ===============

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Color {
    C { red: u8, green: u8, blue: u8 },
    T(u8, u8, u8),
    N(i32),
    D,
}

fn main() {
    println!("Hello, world!");
    let a = Color::C {
        red: 10,
        green: 100,
        blue: 125,
    };
    println!("{}", serde_json::to_string(&a).unwrap());
}

```
Wait! The code fails to build. Let's look at the error message.


```rust
Compiling playground v0.0.1 (/playground)
error: #[serde(tag = "...")] cannot be used with tuple variants
 --> src/main.rs:7:5
  |
7 |     T(u8, u8, u8),
  |     ^^^^^^^^^^^^^

error[E0277]: the trait bound `Color: Serialize` is not satisfied
    --> src/main.rs:19:42
     |
19   |     println!("{}", serde_json::to_string(&a).unwrap());
     |                                          ^^ the trait `Serialize` is not implemented for `Color`
     | 
    ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.61/src/ser.rs:2221:17
     |
2221 |     T: ?Sized + Serialize,
     |                 --------- required by this bound in `serde_json::to_string`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`

To learn more, run the command again with --verbose.
```

Removing the tuple variant makes it work again! I haven't really spent time figuring
out what's gone wrong here.

```rust
// Using just serde `tag` and no `content`

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Color {
    C { red: u8, green: u8, blue: u8 },
    
    // commenting out the tuple variant
    // T(u8, u8, u8),
    
    N(i32),
    D,
}

fn main() {
    let a = Color::C {
        red: 10,
        green: 100,
        blue: 125,
    };
    println!("{}", serde_json::to_string(&a).unwrap());
    // {"type":"C","red":10,"green":100,"blue":125}
}
```

If you don't want any tag information creeping in the serialized string,
choose the `untagged` representation. While deserializing, serde would try
each variant and returns the first one that succeeds.

```rust
// Untagged version

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Color {
    C { red: u8, green: u8, blue: u8 },
    T(u8, u8, u8),
    N(i32),
    D,
}

fn main() {
    let a = Color::C {
        red: 10,
        green: 100,
        blue: 125,
    };
    println!("{}", serde_json::to_string(&a).unwrap());
    // {"red":10,"green":100,"blue":125}
}

```

## Aliasing and renaming fields
You can change the case for individual or all of the fields while serializing
to json representation.

```rust
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Address {
    id: i64,
    street_name: String,
    zip_code: String,
    city: String,
}

let address = Address {
    id: 1,
    street_name: "ABC street".to_owned(),
    zip_code: "10101010".to_owned(),
    city: "XYZ City".to_owned(),
};
let js = serde_json::to_string(&address).unwrap();
// js represented as:
// `{"id": 1, "streetName": "ABC street", "zipCode": "10101010", "city": "XYZ City"}`

```
Fields can also be aliased by multiple names while deserializing the json string.
e.g. field attribute: `#[serde(alias = "<Choose your field alias>")]`

You can avoid serializing or deserializing a field. The attributes to consider are the following:
`#[serde(skip_deserializing)]`, `#[serde(skip_serializing)]`, `#[serde(skip)]`
```rust
#[derive(Serialize, Deserialize, Debug)]
struct XYZP {
    #[serde(alias = "gender")]
    #[serde(alias = "category")]
    sex: String,
    
    #[serde(rename = "blah")]
    age_years: u8,
    
    #[serde(skip)]
    weight_kg: u8,
}

let xyzp = XYZP {
    sex: "male".to_owned(),
    age_years: 25,
    weight_kg: 76,
};

println!("{}", serde_json::to_string(&xyzp).unwrap());
// {"sex":"male","blah":25}

println!(
    "{:?}", 
    serde_json::from_str::<XYZP>(r#"
        {"gender":"male","blah":25}
        "#
    ).unwrap());
// XYZP { sex: "male", age_years: 25, weight_kg: 0 }
    
```