Serde
02 / 02

Serde: Enums, flatten, Custom Logic & Other Formats

Serde: Enums, flatten, Custom Logic & Other Formats

Enums

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Event {
    Click { x: i32, y: i32 },
    KeyPress { key: String },
}

// {"type": "Click", "x": 10, "y": 20}
// The default (no tag attribute) representation nests each
// variant's data under its variant name as the JSON key

flatten: Merging Nested Fields

#[derive(Serialize, Deserialize)]
struct Address { city: String, zip: String }

#[derive(Serialize, Deserialize)]
struct Customer {
    name: String,
    #[serde(flatten)]
    address: Address,
}

// {"name": "Ada", "city": "London", "zip": "E1"} --
// Address's fields merge into Customer's level, not nested

Guarding Against Unexpected Fields

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct StrictConfig {
    port: u16,
}

// Fails deserialization if the input JSON has any field
// StrictConfig doesn't define -- catches typos/schema drift

Working With Unknown Shapes: serde_json::Value

use serde_json::Value;

let parsed: Value = serde_json::from_str(json_str)?;
if let Some(name) = parsed["name"].as_str() {
    println!("{name}");
}

// Useful before committing to a concrete typed struct,
// or for genuinely dynamic/partially-unknown JSON

Beyond JSON

The same derived struct can serialize to a compact binary format (bincode) for fast internal service-to-service communication, or to YAML/TOML for human-readable config files -- Serde's core abstractions aren't tied to any single format or domain, which is why it's foundational across much of the Rust ecosystem.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free