JSON to Rust
No content yet
Free online JSON to Rust tool. Paste API response JSON to auto-generate Serde derive structs, nested objects independently split, Vec and Option smartly inferred, pure browser local conversion, no registration needed.
No content yet
Free online JSON to Rust tool. Paste API response JSON to auto-generate Serde derive structs, nested objects independently split, Vec and Option smartly inferred, pure browser local conversion, no registration needed.
JSON to Rust is the process of converting JSON-formatted data (objects or arrays) into Rust struct type definitions. Rust is a strongly-typed systems programming language widely used for backend services, CLI tools, WebAssembly, embedded, and blockchain scenarios. In development you often need to convert JSON samples from API docs or actual responses into Rust types; hand-writing structs is not only repetitive but also prone to field type errors. This tool aims to automate that process so developers can focus on business logic.
This tool runs locally in the browser based on quicktype-core, using just-types and no-comments rendering options to generate clean struct code for Rust. Output includes use serde::{Serialize, Deserialize}; and corresponding #[derive(Serialize, Deserialize)], directly usable with serde_json for deserialization. This means what you get is not just a type draft, but Rust code that can be placed in a Cargo project and compiled/running with minimal dependency configuration.
Type inference is the core of JSON to Rust. The tool maps JSON primitive types to Rust standard types: strings to String, integers to i64, floats to f64, booleans to bool, arrays to Vec<T>, nested objects to independent structs, null values to Option<serde_json::Value>. For nested objects, the tool automatically creates new structs for each level, named by capitalizing field first letters, e.g., an address field generates an Address struct, objects in items arrays generate an Item struct.
Unlike some online tools that require uploading JSON to servers for processing, all computations for this tool happen in your browser. quicktype-core loads and executes via Web Worker; JSON parsing, type inference, Rust code generation, and file downloads all happen locally, with no data sent to any server. This is especially important for JSON containing API keys, user privacy fields, or unreleased business structures; data is cleared from memory when you close the page, making it a secure choice for enterprise intranet JSON deserialization.
Generated code is typically used in Cargo projects. You need to add serde = { version = "1", features = ["derive"] } and serde_json = "1" dependencies in Cargo.toml, copy generated structs to .rs files in the src directory, and import via mod declarations. Then you can use serde_json::from_str::<User>(&json_string) to convert real API responses into strongly-typed structs, enjoying Rust's compile-time type checking. For mainstream Rust web frameworks like Actix-web, axum, Rocket, Warp, generated structs can directly serve as handler parameters and response models.
Note that auto-generated code is a starting point, not an endpoint. The tool infers types from JSON samples and cannot determine precise business types (e.g., semantic types like URL, Email, ID, timestamps are all uniformly inferred as String or i64). For snake_case JSON fields, Rust struct fields are generated as-is; you may need to manually add #[serde(rename = "user_name")] to maintain deserialization mapping, or change String to semantic types like chrono::DateTime<Utc>, uuid::Uuid, url::Url. We recommend using generated results as first drafts, then fine-tuning field names, types, and annotations according to project conventions.
Use Actix-web 4.x with the generated struct as web::Json<T> parameter; HTTP POST request body auto-deserialized.
// Cargo.toml
// [dependencies]
// actix-web = "4"
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
// Auto-generated by this tool (from JSON)
#[derive(Serialize, Deserialize, Debug)]
pub struct CreateUserRequest {
name: String,
email: String,
age: i64,
tags: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct User {
id: i64,
name: String,
email: String,
age: i64,
tags: Vec<String>,
}
// Actix-web handler: JSON input auto-deserialized
async fn create_user(payload: web::Json<CreateUserRequest>) -> impl Responder {
let req = payload.into_inner();
let user = User {
id: 1,
name: req.name,
email: req.email,
age: req.age,
tags: req.tags,
};
HttpResponse::Ok().json(user) // Response also serialized with serde_json
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/users", web::post().to(create_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
/*
* Client call:
* curl -X POST http://127.0.0.1:8080/users \
* -H "Content-Type: application/json" \
* -d '{"name":"Alice","email":"a@b.com","age":30,"tags":["rust","actix"]}'
*/axum server uses Json<T> extractor, reqwest client uses .json::<T>() to deserialize responses; generated structs are bidirectionally compatible.
// Cargo.toml
// [dependencies]
// axum = "0.7"
// tokio = { version = "1", features = ["full"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
// reqwest = { version = "0.12", features = ["json"] }
use axum::{routing::get, Json, Router};
use serde::{Deserialize, Serialize};
// Auto-generated by this tool
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Address {
city: String,
zip: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct User {
id: i64,
name: String,
email: String,
address: Address,
tags: Vec<String>,
}
// axum handler: returns JSON response
async fn get_user() -> Json<User> {
Json(User {
id: 1,
name: "Alice".into(),
email: "a@b.com".into(),
address: Address { city: "Beijing".into(), zip: "100000".into() },
tags: vec!["rust".into(), "axum".into()],
})
}
// reqwest client: deserialize remote API response
async fn fetch_user(url: &str) -> Result<User, reqwest::Error> {
let user: User = reqwest::get(url).await?.json().await?;
Ok(user)
}
#[tokio::main]
async fn main() {
// Start axum service
let app = Router::new().route("/user", get(get_user));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
/*
* After build, visit:
* curl http://127.0.0.1:8080/user
* {"id":1,"name":"Alice","email":"a@b.com",
* "address":{"city":"Beijing","zip":"100000"},
* "tags":["rust","axum"]}
*/When JSON fields are snake_case but you want camelCase Rust fields, use #[serde(rename)] for explicit mapping and replace semantic types with chrono::DateTime.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Article {
#[serde(rename = "article_id")]
pub article_id: i64,
#[serde(rename = "title")]
pub title: String,
// JSON: "2026-07-08T10:00:00Z" → chrono::DateTime<Utc>
#[serde(rename = "created_at")]
pub created_at: DateTime<Utc>,
#[serde(rename = "view_count")]
pub view_count: i64,
// Missing fields use Default::default()
#[serde(rename = "summary", default)]
pub summary: Option<String>,
#[serde(rename = "tags")]
pub tags: Vec<String>,
}
fn main() -> Result<(), serde_json::Error> {
let json = r#"{
"article_id": 42,
"title": "Hello Rust",
"created_at": "2026-07-08T10:00:00Z",
"view_count": 1024,
"tags": ["rust", "serde"]
}"#;
let article: Article = serde_json::from_str(json)?;
println!("{:?}", article);
// Article { article_id: 42, title: "Hello Rust",
// created_at: 2026-07-08T10:00:00Z, view_count: 1024, ... }
// Reverse serialization: Rust value → JSON string
let s = serde_json::to_string(&article)?;
println!("{}", s);
Ok(())
}
/*
* Cargo.toml dependencies:
* serde = { version = "1", features = ["derive"] }
* serde_json = "1"
* chrono = { version = "0.4", features = ["serde"] }
*/Use generated struct as Tauri #[tauri::command] parameter type; frontend invoke auto-deserializes by JSON.
// Cargo.toml
// [dependencies]
// tauri = { version = "2", features = [] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use serde::{Deserialize, Serialize};
// Auto-generated by this tool
#[derive(Serialize, Deserialize, Debug)]
pub struct CreateNoteRequest {
pub title: String,
pub content: String,
pub tags: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Note {
pub id: i64,
pub title: String,
pub content: String,
pub tags: Vec<String>,
pub created_at: String, // can manually change to chrono::DateTime<Utc>
}
// Tauri command: called from frontend via invoke('create_note', payload)
#[tauri::command]
fn create_note(payload: CreateNoteRequest) -> Result<Note, String> {
let note = Note {
id: 1,
title: payload.title,
content: payload.content,
tags: payload.tags,
created_at: "2026-07-14T10:00:00Z".to_string(),
};
Ok(note)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![create_note])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
/*
* Frontend call example (TypeScript):
*
* import { invoke } from '@tauri-apps/api/core';
* const note = await invoke<Note>('create_note', {
* payload: { title: 'Hello', content: 'World', tags: ['rust', 'tauri'] }
* });
*/The tool infers basic types like String/i64/f64 from JSON samples, but business scenarios have more precise semantic types like chrono::DateTime<Utc>, uuid::Uuid, url::Url, std::net::IpAddr, rust_decimal::Decimal. Recommend immediately changing these fields to business types after generation so the type system catches more errors.
Don't rename generated user_name field to userName to fit Rust naming conventions; this causes Serde deserialization to fail. The correct approach is to keep snake_case field names + #[serde(rename = "userName")], or add #[serde(rename_all = "camelCase")] on the entire struct.
For backward compatibility with API version iterations (new fields won't break old client deserialization), recommend adding #[serde(default)] to all Option<T> fields or fields that allow defaults. This way even if JSON is missing the field, Default::default() provides fallback, avoiding entire request deserialization failure.
While the tool runs completely locally without sending any data, before pasting we recommend desensitizing or deleting sensitive fields like API keys, access tokens, user ID numbers to avoid memory residue after accidentally closing the browser (though closing the page clears memory). This is good security hygiene.
When JSON nesting depth is ≥ 3, the tool generates multiple nested structs. Recommend placing root struct in src/models/user.rs and nested structs (like Address, Company) in src/models/ subdirectory, organized via mod declarations for easier large project maintenance.
Generated code defaults to Rust-spec compliant, but clippy may suggest improvements (like recommending changing String to &str, i64 to usize, etc.). Running cargo clippy --all-targets -- -D warnings can find potential type optimization points.
Structs generated by this tool default to String/Vec<T>, relying on std heap allocation. In embedded/no_std environments, manually replace with &'static str, heapless::String, NVec<T, N> and other heap-free allocation types, and ensure corresponding types implement serde::Deserialize.
When single JSON exceeds 5MB, browser WASM processing slows and memory usage is high. Recommendations: ① Split JSON by business module; ② Convert each module separately with this tool; ③ Recombine in Cargo project. This ensures conversion speed and code maintainability.
Paste JSON content into the left editor, or click the upload button to select a .json/.txt file. You can also click 'Sample' to load the built-in sample. The tool automatically calls quicktype-core within 400ms to generate Rust code, showing Serde-derived structs on the right. If JSON is malformed, click the 'Fix JSON' button to auto-repair before converting.
Yes. The tool uses quicktype-core's just-types rendering mode, generating use serde::{Serialize, Deserialize}; and #[derive(Serialize, Deserialize)] for Rust, directly usable with serde_json for deserialization. If you don't need Serde, you can manually remove the derive and use statements.
All valid JSON structures are supported: primitive types (null, boolean, number, string), arrays (1D or multi-dimensional), nested objects (any depth). JSON objects generate a root struct; JSON arrays generate a root type of Vec<T>. Non-JSON values like JavaScript object literals, functions, Symbol, undefined are not supported.
Strings map to String, integers to i64, floats to f64, booleans to bool, arrays to Vec<T>, nested objects to independent struct, null to Option<serde_json::Value>. See the 'JSON Type to Rust Type Mapping Cheat Sheet' below for specific mapping rules.
null values in JSON generate Option<serde_json::Value>, indicating the field may be missing or have uncertain type. If you already know the actual field type, you can provide an example value in the source JSON (e.g., "field": "" infers String), then change it to a more precise type like Option<String>, Option<i64> after generation based on business needs.
Yes. JSON arrays are uniformly converted to Rust Vec<T> generics, with element types automatically inferred from the first array item. For example, ["a","b"] generates Vec<String>, [1,2,3] generates Vec<i64>, [{...},{...}] generates Vec<Item>. Empty arrays [] default to Vec<serde_json::Value>, which can be manually changed to specific types after generation based on context.
Each nested object generates an independent struct, named by capitalizing the first letter of the field name. For example, if the root object contains an address field, both Root and Address structs are generated, referenced in Root via address: Address. Objects with the same structure reuse the same type to avoid duplicate definitions.
quicktype-core defaults to using the JSON source name (like User) as the root struct name. You can manually modify struct names and reference locations after generation. The downloaded .rs filename can also be renamed as needed when saving. If you want the tool to auto-name by a specific field, adjust the JSON top-level key before pasting.
Yes, it can be used directly, but ensure the project has serde and serde_json dependencies added. In Cargo.toml add serde = { version = "1", features = ["derive"] } and serde_json = "1", then you can use serde_json::from_str::<User>(&json) or serde_json::from_reader(reader) to deserialize the generated structs.
Yes. Both Actix-web 4.x and axum 0.7+ JSON extractors are based on serde_json; the generated struct directly serves as handler parameters. For example in axum: async fn create_user(Json(payload): Json<CreateUserRequest>) -> ...; in Actix-web: web::Json<CreateUserRequest>. No additional annotations needed.
Yes. reqwest's response.json::<T>() internally calls serde_json; pass in the generated struct type: let user: User = reqwest::get(url).await?.json().await?;. If backend API fields are snake_case while Rust fields are camelCase, manually add #[serde(rename = "user_name")].
Yes. Tauri command function parameters are deserialized via serde; use the generated struct as parameter type: #[tauri::command] fn create_user(payload: CreateUserRequest) -> Result<User, String>. When the frontend JS calls invoke('create_user', payload), payload is automatically serialized to JSON then deserialized on the Rust side.
The tool generates basic types like String and i64 by default. If you need semantic types like chrono::DateTime<Utc>, uuid::Uuid, url::Url, std::net::IpAddr, manually adjust field types after generation. These types all implement Serde's Deserialize; just use chrono::{DateTime, Utc}; and they compile.
Runs completely in your local browser. JSON parsing, Rust code generation, and file downloads all happen in the browser via JavaScript and quicktype-core Web Worker; neither input JSON data nor generated Rust code are uploaded to any server, nor logged or cached in the cloud. Sensitive JSON containing API keys, tokens, or unreleased business fields is safe to use; data is cleared when you close the page.
No. The tool is completely free, no registration, login, or authorization required. Open the page and use it; all features are available locally in your browser.
The tool automatically detects JSON validity; on error, a red error message appears on the right with a 'Fix JSON' button. Clicking it auto-repairs common errors: trailing commas, single quotes replaced with double quotes, missing quote completion for keys, comment removal, etc. After successful repair, it continues generating Rust code.
There's no explicit line limit, but browser parsing and rendering of extremely large JSON will slow down. Recommendations: ① Split JSON and convert in batches; ② Focus on one nesting level at a time; ③ If you need to batch-generate 100+ structs, use the quicktype CLI tool (npm i -g quicktype) or IDE plugins.
Both convert JSON to target language type definitions, but the output forms differ: JSON to Rust generates Serde-derived structs, which are runtime-deserializable data models; JSON to TypeScript generates interface/type declarations, used only for compile-time type checking. Choose based on your tech stack and runtime scenario.
The struct definitions themselves can be no_std (depending only on core, not std). But serde_json under no_std requires using serde_json::de::from_slice and enabling appropriate features, and cannot use String (needs replacement with &str or heapless::String). In embedded scenarios you usually need to manually adjust field types and allocators.
The tool preserves original JSON field names (including snake_case); Rust fields default to snake_case naming convention, so when matching JSON names, inconsistencies won't occur. If field name differences appear, it's usually because the source JSON contains special characters or duplicate keys were merged; we recommend cleaning in a JSON repair tool before converting.
The tool automatically infers corresponding Rust types based on JSON value types:
| JSON Value Example | Generated Rust Type | Description |
|---|---|---|
null | Option<serde_json::Value> | null value type uncertain; wraps serde_json::Value with Option as fallback |
true / false | bool | JSON booleans directly map to Rust bool |
42 | i64 | JSON integers default to i64 (manually change to u32/usize as needed) |
3.14 | f64 | JSON floats default to f64 (f64 recommended for high-precision scenarios) |
"hello" | String | JSON strings map to Rust String (can manually change to &str/chrono::DateTime/uuid::Uuid) |
["a","b"] | Vec<String> | String arrays map to Vec<String> |
[1,2,3] | Vec<i64> | Integer arrays map to Vec<i64> |
[{...},{...}] | Vec<Item> | Object arrays generate corresponding struct from first element, then wrapped with Vec |
[] | Vec<serde_json::Value> | Empty arrays can't infer element type; uses serde_json::Value as fallback, recommend manual change to concrete type after generation |
{...} nested object | Independent struct | Nested objects generate independent structs, field names capitalized (e.g., address→Address) |
{ "key": null } | Option<serde_json::Value> | When field value is null, defaults to Option<serde_json::Value>; can manually change to Option<String> |
Typical code quicktype-core generates for Rust includes the following parts:
| Code Part | Example | Purpose |
|---|---|---|
use serde::{Serialize, Deserialize}; | use serde::{Serialize, Deserialize}; | Import Serde's serialization and deserialization traits |
#[derive(Serialize, Deserialize)] | #[derive(Serialize, Deserialize)] | Have the compiler auto-generate Serde trait implementations |
pub struct User | pub struct User { id: i64 } | Define public struct and fields |
Vec<T> field | scores: Vec<i64> | Represents a JSON array field |
Option<T> field | label: Option<serde_json::Value> | Represents a field that may be null |
Nested struct reference | address: Address | Represents a nested object; type auto-generated by the tool |
Generated structs can be directly used in the following common Rust ecosystem scenarios:
| Framework / Tool | Usage Example | Dependencies Required |
|---|---|---|
Actix-web 4.x | web::Json<User> | actix-web = "4", serde_json = "1" |
axum 0.7 | Json<User> | axum = "0.7", serde_json = "1", tokio = "1" |
Rocket 0.5 | Json<User> | rocket = { version = "0.5", features = ["json"] } |
warp 0.3 | body::json::<User>() | warp = "0.3", serde_json = "1" |
reqwest 0.12 | response.json::<User>().await | reqwest = { version = "0.12", features = ["json"] } |
Tauri 2.x | fn command(payload: User) | tauri = "2", serde = { version = "1", features = ["derive"] } |
Bevy 0.14 | Resource / Component serialization | bevy = "0.14", serde = { version = "1", features = ["derive"] } |
SQLx 0.7 | FromRow + serde fields | sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] } |
This tool only generates #[derive(Serialize, Deserialize)] by default, but real projects often need additional attribute annotations to adapt to business scenarios:
| Attribute | Scope | Purpose |
|---|---|---|
#[serde(rename = "userName")] | Field | Match by original JSON name during deserialization (snake_case ↔ camelCase) |
#[serde(rename_all = "camelCase")] | struct | Batch map all fields to camelCase (affects serialization and deserialization) |
#[serde(default)] | Field | Use Default::default() fallback when field is missing from JSON |
#[serde(skip_serializing_if = "Option::is_none")] | Field | Don't serialize field when None (for cleaning JSON output) |
#[serde(skip)] | Field | Completely skip the field (neither into JSON nor deserialized from JSON) |
#[serde(flatten)] | Field | Flatten nested struct fields to outer JSON (for dynamic fields) |
#[serde(deny_unknown_fields)] | struct | Fail deserialization when JSON contains undeclared fields (strict mode) |
#[serde(tag = "type")] | enum | Distinguish enum variants by JSON field (internal tag mode) |
#[serde(untagged)] | enum | Infer enum variant by JSON content (untagged mode) |
#[serde(alias = "user_name")] | Field | Add additional deserialization aliases for field (compatible with multiple JSON keys) |
All operations of this JSON to Rust tool happen completely locally in your browser: JSON parsing, Rust code generation, and file downloads all execute client-side through the quicktype-core WebAssembly module; no JSON content, uploaded files, or generated code are sent over the network to any server. File uploads use the browser's native FileReader API to read directly into memory without passing through any intermediate services. No Cookie tracking is used, no user input or usage data is collected. After closing or refreshing the page, all input and output content is automatically cleared from memory. Suitable for processing JSON containing API keys, tokens, user privacy data, unreleased business structures, and internal interface fields.