Guide
Rust structs for serde from sample JSON
serde is the standard way to read and write JSON in Rust, and it is driven entirely by struct definitions. Deriving Deserialize is easy; working out the right types, names and attributes for a large API response is not. This generator does that part from a sample, producing structs that compile and deserialise the data you pasted.
What you need in Cargo.toml
The structs use serde’s derive macros and, where a field’s type varies, serde_json::Value. Add serde = { version = "1", features = ["derive"] } and serde_json = "1". If you turn on date types, timestamps become DateTime<FixedOffset> from chrono, which needs chrono = { version = "0.4", features = ["serde"] }.
Names, renames and keywords
Rust fields are snake_case. Keys already in snake_case keep their names; others are converted and get #[serde(rename = "…")] so serde still matches the original key — content-type becomes content_type, userName becomes user_name. Keys that are Rust keywords, such as type, match or ref, gain a trailing underscore (type_) plus the rename, which avoids raw identifiers like r#type in your code.
Missing keys and nulls
A key that is missing from some samples becomes Option<T> with #[serde(default, skip_serializing_if = "Option::is_none")]: deserialising works when the key is absent, and serialising leaves it out rather than writing null. A value that is present but sometimes null is also Option<T>, since serde maps JSON null to None.
Collections and loose values
Arrays become Vec<T>, with nested objects turned into their own structs. Fields that were always null or held mixed types become serde_json::Value, which accepts anything; the use serde_json::Value; line is only added when such a field exists.
Using the structs
With serde_json: let user: User = serde_json::from_str(&text)?;. With reqwest’s json feature: let user: User = client.get(url).send()?.json()?; (add .await in async code). Errors name the field and position that failed, which is far easier to debug than a manual parser.
A worked example
From the sample user, the generator writes Address, Item, Order and User. The coupon that only one order has becomes pub coupon: Option<String> with the default and skip attributes, total is f64 because one order has a decimal, and the always-null unit becomes Option<Value>.
Refining the output
Samples cannot reveal everything. A string field that holds a fixed set of values is better as an enum with #[serde(rename_all = "snake_case")]; IDs may deserve a newtype; and a field that was null in every sample needs its real type. Make those changes, then keep a test that deserialises a real response so you notice when the API changes.
Arrays at the root and many samples
When the sample is an array, the root struct describes one element and a comment says so; deserialise into Vec<User>. For newline-delimited JSON, paste every line at once: each one is merged into the same structs, and a key that appears in only some lines becomes an Option, exactly as serde will need it.
Borrowing instead of owning
The generated fields own their data with String and Vec, which is the simplest choice. For very large documents parsed once and discarded, serde can also borrow string slices from the input with lifetimes (&'a str), saving allocations; convert by hand only when profiling shows it matters.
Related tools
Make Go structs from the same JSON with JSON to Go, frontend types with JSON to TypeScript, Rust reqwest code from a curl example with cURL to Code, or convert a Cargo.toml with the TOML Converter.
Checked against the current version of the JSON to Rust Serde Struct on .

