JSON to Rust Struct
Generate a Rust struct with serde derive attributes from a JSON document.
Related Tools
Generate Go structs with JSON tags from a JSON document.
Generate TypeScript interfaces or type aliases from JSON.
Generate a Kotlin data class from a JSON document.
Beautify and pretty-print JSON with configurable indentation.
Convert a cURL command into a Rust reqwest request.
Generate Python dataclasses from a JSON document.
Documentation
What is JSON to Rust?
This tool converts a JSON sample into Rust structs derived for serde — the de facto standard (de)serialization framework in Rust. Each generated struct derives Debug, Serialize, Deserialize, so it works directly with serde_json::from_str / to_string with no extra glue code.
How it works
Nested JSON objects become their own pub struct, printed child-first. Field names are converted to snake_case, the Rust convention. When that conversion produces a name different from the raw JSON key — isActive becoming is_active — a #[serde(rename = "isActive")] attribute is added above that field so serde still maps to the original wire key. A field whose sample value was null is wrapped in Option<T>. Numbers become i64 or f64 — the generator always picks the widest safe default rather than guessing a smaller type like u32 or f32. A field name that's a Rust keyword (like type or match) is escaped as a raw identifier with r#.
Features
#[derive(Debug, Serialize, Deserialize)]on every generated struct, ready forserde_json- Automatic snake_case field renaming with a matching
#[serde(rename = "...")]so the JSON wire format is untouched - Nullable sample values become
Option<T>, matching how serde represents absent/null JSON - Rust keyword field names are escaped with
r#raw identifiers instead of producing invalid code - Struct name collisions with
String,Vec,Option, orBoxare renamed with aDatasuffix
Example
Input: { "id": 101, "username": "alice_dev", "isActive": true, "signupBonus": null, "address": { "city": "Berlin", "zipCode": "10115" }, "tags": ["admin", "beta"] }
Output:
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Address {
pub city: String,
#[serde(rename = "zipCode")]
pub zip_code: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Root {
pub id: i64,
pub username: String,
#[serde(rename = "isActive")]
pub is_active: bool,
#[serde(rename = "signupBonus")]
pub signup_bonus: Option<serde_json::Value>,
pub address: Address,
pub tags: Vec<String>,
}Common errors
A null sample value produces Option<serde_json::Value> — untyped, since there's nothing concrete to infer. Narrow it to a real type once you know the field's actual shape, or the compiler won't help you catch misuse. serde_json::Value requires the serde_json crate as a dependency even when a field's type is otherwise fully typed. An empty sample array has no element to infer from and falls back to Vec<serde_json::Value>.
Best practices
Add #[serde(rename_all = "camelCase")] at the struct level instead of per-field rename attributes if every field in that struct needs the same camelCase-to-snake_case mapping — it's more concise for structs with many renamed fields. Narrow the numeric types (i32, u32, f32) once you know your actual value ranges — the generator defaults to the widest safe types, which cost more memory than necessary for large collections of structs.
Frequently Asked Questions
Why serde specifically?▾
serde with serde_json is the de facto standard for JSON (de)serialization in Rust — the generated struct derives Serialize and Deserialize so it works directly with serde_json::from_str / to_string with no extra glue code.
Why are field names converted to snake_case?▾
Rust convention is snake_case struct fields. When a JSON key isn't already snake_case (e.g. camelCase from a JS-authored API), the generator adds a #[serde(rename = "...")] attribute so serde still maps to the original JSON key on the wire while your Rust code reads idiomatically.
How are optional/nullable fields handled?▾
A field whose sample value is null is wrapped in Option
What type do numbers get?▾
Whole numbers become i64, numbers with a decimal point become f64. Narrow these to a smaller type (u32, f32, etc.) by hand if your use case needs it — the generator picks the safe, widest default.