JSON Serialization and Deserialization in Rust with Serde

The serde crate is the standard for serializing and deserializing data in Rust. It allows you to convert Rust structs to JSON and back using powerful macros. Combined with serde_json, it gives you type-safe and flexible control over reading and writing structured data.

I first used serde when building a CLI tool that needed to read a configuration file in JSON. I was amazed at how easy it was to map JSON into strongly typed Rust structs. It helped catch mistakes early, and I never had to worry about missing or invalid fields at runtime.

In this post, you will learn how to serialize Rust types to JSON, deserialize JSON into Rust, and customize the process with optional fields, custom keys, and nested data.

Add serde and serde_json to Your Project

In your Cargo.toml:

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

These crates work together: serde handles the logic, and serde_json handles JSON parsing specifically.

Basic Deserialization: JSON to Rust

You can turn a JSON string into a Rust struct:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct User {
    name: String,
    age: u8,
}

fn main() {
    let data = r#"{"name": "Alice", "age": 30}"#;

    let user: User = serde_json::from_str(data).unwrap();
    println!("{:?}", user);
}

This parses the JSON and fills the fields based on name and type. If any field is missing or the type is wrong, you get an error.

Serialization: Rust to JSON

You can turn a struct into a JSON string:

use serde::Serialize;

#[derive(Serialize)]
struct Product {
    name: String,
    price: f64,
}

fn main() {
    let product = Product {
        name: "Book".into(),
        price: 12.99,
    };

    let json = serde_json::to_string(&product).unwrap();
    println!("{}", json);
}

Output:

{"name":"Book","price":12.99}

Use to_string_pretty for readable formatting.

Optional Fields

You can use Option<T> for optional values:

#[derive(Deserialize)]
struct Config {
    theme: Option<String>,
}

This field can be missing in the JSON and will default to None.

Renaming Fields

Use #[serde(rename = “…”)] if the JSON key does not match your field name:

#[derive(Deserialize)]
struct Record {
    #[serde(rename = "user_id")]
    id: u32,
}

This allows you to keep idiomatic Rust field names and still match external formats.

Ignoring Fields

Use #[serde(skip_serializing)] or #[serde(skip_deserializing)] to ignore fields during conversion:

#[derive(Serialize, Deserialize)]
struct Secret {
    name: String,
    #[serde(skip_serializing)]
    password: String,
}

This keeps sensitive or internal data out of the JSON output.

Nested Structs

Serde works with nested structures:

#[derive(Serialize, Deserialize)]
struct Post {
    title: String,
    author: User,
}

#[derive(Serialize, Deserialize)]
struct User {
    name: String,
}

You can map entire JSON trees into nested Rust structs.

Reading JSON from Files

Use fs::read_to_string and from_str:

use std::fs;
use serde::deserialize;

#[derive(Deserialize)]
struct Config {
    debug: bool,
}

fn main() {
    let data = fs::read_to_string("config.json").unwrap();
    let config: Config = serde_json::from_str(&data).unwrap();
    println!("Debug: {}", config.debug);
}

This is perfect for reading settings or local data.

Writing JSON to Files

Use serde_json::to_string_pretty and fs::write:

use serde::Serialize;
use std::fs;

#[derive(Serialize)]
struct Log {
    event: String,
    success: bool,
}

fn main() {
    let log = Log {
        event: "Login".into(),
        success: true,
    };

    let json = serde_json::to_string_pretty(&log).unwrap();
    fs::write("log.json", json).unwrap();
}

This gives you clean, human-readable JSON files.

Summary

The serde crate makes it safe and easy to work with JSON in Rust. You can read config files, interact with APIs, or persist structured data, all while keeping strong type guarantees. With support for optional fields, custom keys, and full control, it is one of Rust’s most mature and widely used tools.