Data & APIs

JSON to Rust with Serde: Structs, Arrays and Renamed Fields

Map API JSON into Rust structs with Serde derives, nested models, Vec fields and explicit JSON-key renaming.

Photorealistic editorial photo for Rust Serde Json Mapping
Data & APIs · Practical ideas for your next build

A Rust API client needs a model that describes the response it actually receives. Serde connects serialized JSON keys to Rust fields, but a generated model is still a hypothesis based on examples. Start with several representative responses, including records with missing values, before choosing required fields and numeric types.

Match wire keys without sacrificing Rust names

A JSON property such as userId can map to a Rust field named user_id through a serde rename attribute. Explicit attributes are useful when keys contain punctuation or when multiple spellings normalize to similar identifiers. Keep the original key in the attribute rather than changing the API contract. Our generator disambiguates colliding Rust names and creates a nested struct for each inferred object shape.

Choose an intentional number representation

An integer-looking sample becomes i64 in this tool, while fractional examples use f64. The tool rejects integer samples outside JavaScript’s safe range because the browser parser cannot reliably preserve their exact digits. If the API represents large identifiers as strings, keep them as String. Amounts requiring decimal arithmetic need a deliberate domain representation; observing one numeric example does not establish a safe money type.

Deserialize the same root shape you inspected

A JSON object can deserialize into Root; an array of objects needs Vec<Root>. An empty array provides no field evidence, so the generator asks for a non-empty record sample. Add tests using real response fixtures and assert the values your application relies on. A successful deserialize establishes structural compatibility for that fixture, while application-level checks still need to validate ranges, relationships and permissions.

Worked example

EXAMPLE
#[derive(serde::Deserialize)]
struct User {
    #[serde(rename = "userId")]
    user_id: i64,
    tags: Vec<String>,
}

Why use serde rename instead of changing the JSON key?

The attribute keeps Rust naming conventions while preserving the external API format. It avoids silently inventing a different wire contract.

Reference and further reading

Read the official documentation ↗