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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Deriving Deserialize on nested structs lets serde build an entire object tree from one call.
- 2Field attributes like rename, default, and flatten bridge the gap between JSON conventions and Rust idioms.
- 3Option and Vec fields model optional and repeated data so missing values never panic.
Related explainers
python
from fastapi import FastAPI, Request, status from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse
Redacting sensitive fields in FastAPI errors
validation
error-handling
security
Intermediate
7 steps
rust
use std::{collections::HashSet, sync::Arc}; use axum::{ body::Body,
Feature-flag middleware in Axum
middleware
async
shared-state
Advanced
7 steps
rust
pub fn format_size(bytes: u64) -> String { const UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; if bytes < 1024 {
Formatting byte counts as human-readable sizes
bit-manipulation
formatting
unit-conversion
Intermediate
5 steps
javascript
import { Component } from 'react'; import { reportError } from './services/telemetry'; export class ErrorBoundary extends Component {
How a React ErrorBoundary works
error-handling
lifecycle-methods
render-props
Intermediate
8 steps
rust
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}; use base64::{DecodeError, Engine}; pub fn encode_standard(data: &[u8]) -> String {
Base64 encode and decode in Rust
base64
encoding
error-handling
Beginner
7 steps
rust
use std::collections::HashMap; #[derive(Debug)] pub struct RequestHead {
Parsing an HTTP request head in Rust
parsing
error-handling
iterators
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/modeling-nested-json-with-serde-in-rust-explained-rust-c7ba/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.