Embed this tool on your site
About this tool
Rust's ownership model and strict type system make it excellent at handling JSON safely, but writing the boilerplate for serde deserialization by hand is one of the more repetitive tasks in Rust development. Every field needs its type, every struct needs a derive line, and snake_case Rust conventions often conflict with camelCase keys in the incoming JSON. JSON to Rust Struct drafts that boilerplate: set a Root Struct Name, paste a JSON object, and receive Rust struct definitions with use serde::{Deserialize, Serialize};, #[derive(Debug, Serialize, Deserialize)], snake_case field names, #[serde(rename = "...")] wherever the field name differs from the JSON key, and type mappings — String, i64, f64, bool, Vec<T> for arrays, nested struct types for nested objects, and serde_json::Value for null or mixed values. The root must be a JSON object; a top-level array is rejected. Arrays of two or more objects currently produce Vec-wrapped fields, so review those structs before use.
Why use it
Eliminates Serde Boilerplate: Writing derive macros, rename attributes, and nested struct definitions for a 30-field API response by hand takes many minutes — the converter drafts it in under a second.
Keyword-Safe Field Names: JSON keys that are Rust keywords or start with a digit get safe names such as type_field or n_2fa, with a rename attribute that keeps the original key.
Serde Rename Attributes Included: camelCase or PascalCase JSON keys get snake_case Rust fields plus a #[serde(rename = "...")] attribute automatically, preventing silent deserialization failures.
Derive and Import Lines Included: Each struct gets #[derive(Debug, Serialize, Deserialize)] and the output starts with the serde use line, plus use serde_json::Value; when needed.
Works Without Rust Installed: Run the converter in any browser — useful during architecture planning, API contract reviews, or on machines that do not have a Rust toolchain installed.
No Data Leaves the Browser: The conversion runs entirely client-side. API response samples that may contain sensitive fields or proprietary data structures are never transmitted to any server.
How to use
- Set the Root Struct Name (Root by default).
- Paste a JSON object into the JSON Input box — the root must be an object, and a real, fully-populated API response gives the most accurate type mapping.
- Click Convert. Strings map to String, whole numbers to i64, decimals to f64, booleans to bool, and null to serde_json::Value.
- Check the field names: they are emitted in snake_case, and when the JSON key differs (e.g. userId) a #[serde(rename = "userId")] attribute is added.
- Review nested structs (named parent + key, e.g. UserAddress) and Vec<T> fields, and replace Value with Option<T> where a field can be null.
- Copy the code into your Rust source file and use serde_json::from_str(&json_string) to deserialize your data.
When it helps
- When building a Rust web service with Axum or Actix-Web that receives or returns JSON and you need serde structs for the request and response bodies.
- When writing a Rust CLI tool that fetches data from a REST API and you want strongly-typed deserialization instead of working with serde_json::Value.
- When onboarding a new API integration in an existing Rust project and you have a real JSON response to work from but no official Rust types provided.
- When porting a Go, Python, or TypeScript service to Rust and you need to replicate the JSON data model as serde-compatible Rust structs.
- When prototyping a new feature where you want to start with typed structs before writing business logic, using a fixture JSON response as the type source.
- When auditing an undocumented internal API by capturing a real response payload and immediately generating usable Rust types from it.
Examples
01Simple user object (Root Struct Name: User)
Input{
"userId": 1,
"displayName": "rustacean",
"active": true,
"score": 98.6
}
Expected result · Scroll for moreuse serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "userId")]
pub user_id: i64,
#[serde(rename = "displayName")]
pub display_name: String,
pub active: bool,
pub score: f64,
}
02Nested object with a null field (Root Struct Name: User)
Input{"name":"Alice","address":{"street":"123 Main St","unit":null}}
Expected result · Scroll for moreuse serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub name: String,
pub address: UserAddress,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserAddress {
pub street: String,
pub unit: Value,
}
03Keyword and digit keys (Root Struct Name: Post)
Input{"post_id":7,"tags":["rust","serde"],"type":"article","2fa":false}
Expected result · Scroll for moreuse serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Post {
pub post_id: i64,
pub tags: Vec<String>,
#[serde(rename = "type")]
pub type_field: String,
#[serde(rename = "2fa")]
pub n_2fa: bool,
}
Tips
- Add #[serde(rename_all = "camelCase")] at the struct level when the JSON uses consistent camelCase keys — this removes the need for per-field rename attributes and keeps the generated code much shorter.
- For date-time string fields like '2026-05-07T12:00:00Z', change the generated String type to chrono::DateTime<Utc> (with the chrono serde feature enabled) to get automatic parsing and type safety.
- Replace generated Value fields with Option<T> and add #[serde(skip_serializing_if = "Option::is_none")] so null keys are omitted from serialized JSON when the value is absent.
- To deserialize a top-level JSON array, convert one element as an object, then use Vec<Root> in your code: let items: Vec<Root> = serde_json::from_str(json)?;
- Run cargo fmt on the generated code after pasting — serde derive attributes and struct field alignment benefit from rustfmt's automatic formatting to match community style.
Frequently Asked Questions
What serde version does the generated code target?⌄
The output is compatible with serde 1.x and serde_json 1.x, which are the stable current versions. Add serde = { version = "1", features = ["derive"] } and serde_json = "1" to your Cargo.toml to use the generated structs.
How are JSON numbers mapped to Rust types?⌄
Whole numbers (e.g. 42) map to i64 to handle the full range of JSON integer values. Decimal numbers (e.g. 3.14) map to f64. If you know a field will always be a small non-negative integer, you can manually change i64 to u32 or usize after generation.
How are null and missing JSON fields handled?⌄
A null value is typed as serde_json::Value (and the use serde_json::Value; import is added), because a single null gives no hint of the real type. Change it to Option<String> or another Option<T> by hand, optionally with #[serde(skip_serializing_if = "Option::is_none")] to omit None values when serializing.
What happens with camelCase JSON keys?⌄
camelCase keys like userId or createdAt are emitted as snake_case field names (user_id, created_at) and annotated with #[serde(rename = "userId")] to preserve correct deserialization. Alternatively, you can add #[serde(rename_all = "camelCase")] at the struct level and remove per-field renames.
Does the output include Serialize or just Deserialize?⌄
Both. Each struct gets #[derive(Debug, Serialize, Deserialize)] and the output starts with use serde::{Deserialize, Serialize};. Clone is not derived — add it to the derive list if you need to clone the structs.
How are nested JSON objects handled?⌄
Each nested object produces a separate named Rust struct, named by joining the parent struct name and the key (an address key inside User becomes UserAddress). The parent holds the nested struct by value, which is the idiomatic serde pattern for owned deserialized data.
What type is used for JSON arrays?⌄
Arrays produce Vec<T>: Vec<String>, Vec<i64>, Vec<f64> (for mixed whole and decimal numbers) or Vec<StructName> for objects, and Vec<Value> for empty or mixed arrays. An array holding a single object works as expected, but an array of two or more objects currently wraps every field of the item struct in Vec<...>, so remove those Vec wrappers by hand.
Can I use the generated structs with Axum or Actix-Web extractors?⌄
Yes. Axum's Json<T> extractor and Actix-Web's web::Json<T> both use serde_json internally. Any struct with #[derive(Deserialize)] can be used directly as an extractor type without additional configuration.
Glossary
- serde
- A Rust framework for serializing and deserializing data structures. The #[derive(Serialize, Deserialize)] macros from serde generate code to convert Rust structs to and from formats like JSON, TOML, and MessagePack.
- serde_json
- The serde data format implementation for JSON. Provides serde_json::from_str(), serde_json::to_string(), and serde_json::Value for working with JSON data in Rust.
- Option<T>
- Rust's nullable type — Option::Some(T) holds a value and Option::None represents absence. Used to represent JSON null values and optional fields in serde structs.
- #[serde(rename)]
- A serde field attribute that maps a Rust struct field name to a different JSON key. Used when the JSON key does not match the snake_case Rust field name.
- #[serde(rename_all)]
- A serde struct-level attribute that applies a naming convention to all fields at once. Common values: "camelCase", "PascalCase", "snake_case". Avoids per-field rename attributes for consistently-cased JSON.
- Derive Macro
- A Rust procedural macro invoked with #[derive(...)] that auto-generates trait implementations for a struct. serde's derive macros generate the Serialize and Deserialize implementations at compile time.
A LITTLE HELP. A LOT DONE.
Good tools.
Great flow.
Your shortcut to the everyday. Create, convert, calculate,
and get back to what you love.
✳
✳
✓ Free to use✓ No sign-up✓ 500+ possibilities