JSON to Rust Struct — Generate Rust Struct Types from JSON
Generate Rust struct types from JSON with serde. Option for nullable, rename_all for snake_case, #[serde(default)] for absent — in your browser, no upload.
JSON to Rust Struct
Convert JSON to Rust struct with serde derive macros. Works entirely in your browser.
Rust’s type system is famously strict, and serde is the bridge that makes it practical to work with JSON. The Deserialize derive macro is a code generator that writes the parsing logic for you: a struct with #[derive(Deserialize)] can be instantiated from a JSON string with a single serde_json::from_str::<MyStruct>(&json_str)? call, and the compiler checks that every field type is deserializable before the code runs. The JSON-to-Rust struct generator walks a sample JSON response and emits the struct that matches it — a task that takes five minutes by hand (renaming fields from camelCase to snake_case, choosing Option vs non-Option, setting default values) and five milliseconds by machine.
The non-obvious Rust-specific concerns are the naming convention bridge and the default/optional distinction. Rust’s convention is snake_case (user_id), and JSON’s convention varies — most APIs use camelCase (userId), some use PascalCase (UserId), and internal APIs may use snake_case natively. serde’s rename_all attribute handles the case conversion globally. The Option vs #[serde(default)] distinction is more subtle: Option<T> covers the case where a field is present but null, and #[serde(default)] covers the case where the field is entirely absent from the JSON object. Most APIs need both.
The most common downstream step after generating a struct is to pass it to reqwest or ureq for API consumption: deserialize the response body directly with .json::<MyStruct>() and let serde handle the parsing. For async Rust with Tokio, wrap the struct in Arc<RwLock<MyStruct>> for shared mutable access. For databases, add #[derive(sqlx::FromRow)] or Diesel’s #[derive(Queryable)] alongside the serde derives. The generated struct is the data contract — the async runtime, the database mapping, and the error handling are the parts that the developer adds.
How to use
Paste your JSON
Drop a JSON object or array of objects into the input. The generator walks the structure and emits a Rust struct for the root, with nested structs for each object shape.
Choose serde options
Toggle `rename_all = camelCase` for JSON keys in camelCase, `Option<T>` for nullable fields, and `#[serde(default)]` for fields that may be absent in some API responses.
Copy or download the .rs file
Copy the Rust struct to your clipboard or download as a .rs file. The output includes `use serde::{Deserialize, Serialize};` at the top — paste into a module and it compiles.
Frequently asked
Why does it derive Deserialize and Serialize?
serde's `#[derive(Deserialize, Serialize)]` is the standard way to opt into automatic JSON serialization in Rust. `Deserialize` is required for parsing JSON into the struct at runtime; `Serialize` is optional but included by default so the struct round-trips correctly.
How does rename_all handle JSON key case?
`#[serde(rename_all = "camelCase")]` at the struct level tells serde to map Rust's snake_case field names (`user_id`) to the JSON's camelCase keys (`userId`). The rename applies to all fields. Individual fields can override with `#[serde(rename = "custom_name")]`.
How are null and absent fields handled?
Fields that are null in the sample become `Option<T>`. Fields that are absent in some array elements get `#[serde(default)]` in addition to `Option<T>`, so a missing key in the JSON deserialises to `None` instead of causing a parse error.
What about enums and tagged unions?
The generator does not infer Rust enums from a set of known string values. A status field that holds "active", "pending", or "closed" is emitted as `String`. Define the enum and the serde tag by hand after generation.
Does it handle chrono and uuid types?
By default, ISO 8601 date strings become `String`. Toggle 'chrono' to emit `chrono::NaiveDateTime` and add the `serde::with` annotation for the ISO 8601 format. UUID strings become `String` by default — add the `uuid` crate by hand if you use it.
Limitations
- No serde flattenThe generator does not use `#[serde(flatten)]` for nested JSON objects that should be inlined at the parent level. Add flattening by hand for deeply nested APIs that your application treats as flat.
- No generic type parametersThe output is a concrete struct with concrete field types. A polymorphic JSON response (e.g. `{ "type": "image", "data": ... }`) requires a hand-written enum with serde's internally tagged representation.
- Borrowing not consideredAll field types own their data (String, Vec, ...). No lifetimes or borrows are used. For zero-copy deserialization with `Cow<'_, str>`, add the lifetimes and the `#[serde(borrow)]` annotation by hand.
Platform notes
- macOS
- RustRover, VS Code with rust-analyzer, and vim with Rust support all handle the generated struct. Use the browser tool for one-off generation from a sample response during API exploration.
- Windows
- The generated struct compiles on any Rust toolchain. Use the browser tool for one-off struct generation without opening a full IDE.
- Linux
- For command-line work, `quicktype --lang rust` or `serde-generate` produce similar output. The browser tool is the right pick for one-off generation where a Rust installation is not available.
- Web
- Runs entirely client-side. The generation is a few milliseconds.