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 keyflatten: 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 nestedGuarding 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 driftWorking 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 JSONBeyond 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