JSON to Rust Struct Generator
Paste JSON, get serde-ready Rust structs — Option<T>, derives, rename_all — copy or download as .rs
Options
444 characters
4 structs generated · 764 characters
You’re integrating a REST API in Rust with reqwest and serde. The JSON response has 30 fields, nested objects, arrays of objects, and fields that are null in some responses. Writing the struct by hand means 30 fields with #[serde(rename = "field_name")] attributes, Option<T> for nullable fields, and #[derive(Serialize, Deserialize)] — one wrong type annotation and deserialization silently fails or panics.
Why This Generator (Not json-to-go)
PureDevTools has JSON-to-code generators for Go, TypeScript, Python, and SQL. This tool generates serde-ready Rust structs with Serialize/Deserialize derives, Option<T> for nullable fields, pub visibility, and rename_all attribute support. Everything runs in your browser; no data is sent anywhere.
What Is a JSON to Rust Struct Generator?
When building Rust applications that consume REST APIs, parse configuration files, or process JSON data, you need Rust struct types that exactly match the JSON structure. Writing these structs by hand — with serde attributes, correct types, and Option wrapping — is tedious and error-prone.
A JSON to Rust struct generator reads your JSON and automatically produces correct Rust struct declarations with serde support, handling:
- Nested objects (each becomes its own named struct)
- Arrays of objects (all objects merged into a single struct)
- Primitive values (
String,i64,f64,bool) nulland missing fields mapped toOption<T>- Automatic
#[serde(rename = "...")]when field names don’t match JSON keys #[derive(Serialize, Deserialize)]and other common derives
This tool runs entirely in your browser — your JSON data never leaves your device.
How to Use This Tool
- Paste your JSON in the JSON Input textarea (or click Load sample to try an example)
- Configure options: root struct name, derive macros, pub visibility, rename_all, Option wrapping
- The Rust struct output updates instantly in the Rust Output panel
- Click Copy to copy to clipboard, or Download .rs to save as a
.rsfile
Understanding the Generated Code
Field Naming
JSON keys are converted to idiomatic Rust snake_case field names:
camelCase→camel_case(split on case boundaries)snake_case→snake_case(unchanged)kebab-case→kebab_case(hyphen → underscore)PascalCase→pascal_caseALL_CAPS→all_caps- Keys starting with digits get a
field_prefix:123key→field_123key - Rust keywords get a
_fieldsuffix:type→type_field
Struct Naming
Nested struct names are derived from the parent struct name plus the field name in PascalCase:
- Root object → name you specify (default:
Root) - Nested field
address→RootAddress - Nested field
socialinsideprofile→RootProfileSocial - Array element structs → parent name +
Item: e.g.,RootPostsItem
Type Mapping
| JSON type | Rust type |
|---|---|
string | String |
| integer number | i64 |
| float number | f64 |
boolean | bool |
null | Option<serde_json::Value> |
object | Named struct |
array (uniform) | Vec<T> |
array (mixed) | Vec<serde_json::Value> |
array (empty) | Vec<serde_json::Value> |
Options Explained
Derive Macros
Check or uncheck each derive macro to include in #[derive(...)] on every generated struct:
- Debug — enables
{:?}formatting. Almost always useful. - Clone — allows
.clone(). Recommended for most use cases. - Serialize — serde JSON serialization. Enables conversion from Rust to JSON.
- Deserialize — serde JSON deserialization. Enables parsing JSON into Rust structs.
- PartialEq — enables
==and!=comparisons between structs. - Default — provides a
Default::default()implementation with zero values.
When Serialize or Deserialize is selected, the tool adds the appropriate use serde::... import at the top of the generated code.
pub Visibility
When enabled (default), all generated structs and fields are marked pub:
pub struct Root {
pub name: String,
pub age: i64,
}
Disable to generate private structs (useful when you control visibility through a module boundary).
rename_all
Controls the #[serde(rename_all = "...")] attribute placed on every struct. This tells serde how to transform Rust field names into JSON keys automatically:
| Option | Rust field | JSON key |
|---|---|---|
none (default) | first_name | first_name |
camelCase | first_name | firstName |
snake_case | first_name | first_name |
SCREAMING_SNAKE_CASE | first_name | FIRST_NAME |
When the automatic transformation doesn’t produce the correct JSON key, the tool adds a per-field #[serde(rename = "original_key")].
Common use case: JavaScript APIs often use camelCase JSON. Setting rename_all = "camelCase" lets you use idiomatic snake_case Rust field names while serde transparently maps them to camelCase for serialization.
Option<T> for nullable
When enabled (default), null JSON values and fields missing in some array elements become Option<T>:
// JSON: {"name": "Alice", "bio": null}
pub name: String,
pub bio: Option<serde_json::Value>,
Disable to use serde_json::Value directly for null fields (less type-safe but avoids Option unwrapping).
Working with Arrays
Array of Objects
When all array elements are plain objects, they are merged into a single named struct. Fields present in only some elements become Option<T> when the nullable option is enabled:
{"posts": [{"id": 1, "title": "A"}, {"id": 2, "views": 100}]}
Generates:
pub struct Root {
pub posts: Vec<RootPostsItem>,
}
pub struct RootPostsItem {
pub id: i64,
pub title: Option<String>, // only in first element
pub views: Option<i64>, // only in second element
}
Uniform Primitive Arrays
{"tags": ["rust", "serde"], "ids": [1, 2, 3]}
Generates Vec<String> and Vec<i64> respectively.
Mixed Arrays
Arrays with mixed types (e.g., [1, "hello", true]) generate Vec<serde_json::Value>.
Root-Level Values
Root Array
[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
Generates:
pub type Root = Vec<RootItem>;
pub struct RootItem {
pub id: i64,
pub name: String,
}
Root Primitive
"hello"
Generates:
pub type Root = String;
Adding serde to Your Project
After generating your structs, add serde to Cargo.toml:
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Then deserialize JSON in your code:
use serde_json;
let data: Root = serde_json::from_str(json_str)?;
Common Use Cases
Parsing REST API Responses
When integrating with a REST API, paste a sample response to instantly generate Rust types you can use with reqwest and serde_json:
let response = client.get(url).send().await?;
let data: Root = response.json().await?;
Configuration File Parsing
Paste your config JSON, set the root name to something meaningful (Config, AppConfig), and get deserialization code ready to use with std::fs::read_to_string and serde_json::from_str.
Database JSON Columns
When working with jsonb columns in PostgreSQL via sqlx or diesel, generate the Rust types matching your stored JSON schema.
API Client Generation
Use the generated structs as the response types for hand-written or generated API clients.
After Generating
The generated code is a starting point. Consider these refinements before committing:
- Integer types:
i64is safe but you may preferu32,u64, orusizefor specific fields - Date/time fields: string fields that contain dates may benefit from
chrono::DateTime<Utc>with#[serde(with = "...")] - Module structure: move structs to appropriate modules (
models,types,api) - Validation: add
#[serde(deny_unknown_fields)]if you want strict parsing - Custom deserialization: some fields may need custom
Deserializeimplementations for special formats
FAQ
What crates do I need?
Add to Cargo.toml:
serde = { version = "1", features = ["derive"] }
serde_json = "1" # only needed if any fields use serde_json::Value
Why does the tool use i64 instead of u64 or u32?
i64 is the safest choice for unknown JSON numbers — it can represent any integer value that fits in a JSON number without overflow. After generating, adjust to a more specific type (u32, usize, i32) if you know the valid range.
How do I handle enums in JSON?
JSON strings that represent enum variants (e.g., "status": "active") are generated as String. After generating, you can replace String with a custom serde-compatible enum:
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Status {
Active,
Inactive,
Pending,
}
Why are some fields wrapped in Option even if they have values in my JSON?
If a field is present in some objects of an array but absent in others, the tool wraps it in Option<T> to accurately model the optional nature of the field. You can disable this behavior by unchecking “Option<T> for nullable”.
Can I generate multiple separate struct files?
The tool generates a single block of Rust code. You can split it manually — each struct block (#[derive(...)] pub struct Foo { ... }) can be placed in its own file, with appropriate pub use re-exports if needed.
Why is my null field Option<serde_json::Value> instead of Option<String>?
When serde sees null, it cannot infer the intended type from the JSON alone. Option<serde_json::Value> is the safest fallback that can hold any future non-null value. If you know the actual type, replace it manually: Option<String>, Option<i64>, etc.