rust 51 lines · 9 steps

Modeling nested JSON with serde in Rust

A tree of derived structs maps a messy paginated API response onto strongly-typed Rust values.

Explained by highlit
1use serde::Deserialize;
2use serde_json::Value;
3use std::collections::HashMap;
4 
5#[derive(Debug, Deserialize)]
6struct ApiResponse {
7 status: String,
8 #[serde(default)]
9 data: PageData,
10}
11 
12#[derive(Debug, Default, Deserialize)]
13struct PageData {
14 users: Vec<User>,
15 #[serde(rename = "nextPage")]
16 next_page: Option<u32>,
17}
18 
19#[derive(Debug, Deserialize)]
20struct User {
21 id: u64,
22 #[serde(rename = "fullName")]
23 full_name: String,
24 #[serde(default)]
25 roles: Vec<String>,
26 profile: Profile,
27 #[serde(flatten)]
28 extra: HashMap<String, Value>,
29}
30 
31#[derive(Debug, Deserialize)]
32struct Profile {
33 #[serde(default)]
34 bio: String,
35 address: Option<Address>,
36}
37 
38#[derive(Debug, Deserialize)]
39struct Address {
40 city: String,
41 #[serde(rename = "postalCode")]
42 postal_code: String,
43}
44 
45fn parse_users(raw: &str) -> Result<Vec<User>, serde_json::Error> {
46 let response: ApiResponse = serde_json::from_str(raw)?;
47 if response.status != "ok" {
48 return Ok(Vec::new());
49 }
50 Ok(response.data.users)
51}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Deriving Deserialize on nested structs lets serde build an entire object tree from one call.
  2. 2Field attributes like rename, default, and flatten bridge the gap between JSON conventions and Rust idioms.
  3. 3Option and Vec fields model optional and repeated data so missing values never panic.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Modeling nested JSON with serde in Rust — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code