★ Rust (serde) · from JSON
JSON to Rust (serde) Converter
Serde structs with idiomatic snake_case fields, #[serde(rename)] where the JSON key differs, Option for anything that can be missing or null, and raw identifiers for keys like type.
- Runs in your browser
- No signup
Convert to
Strictness
Your JSON
Rust (serde)
3 types · 16 fieldsuse serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
pub id: i64,
pub email: String, // email
#[serde(rename = "display-name")]
pub display_name: String,
#[serde(rename = "createdAt")]
pub created_at: String, // date-time
pub plan: serde_json::Value,
pub tags: Vec<String>,
pub team: Team,
pub invoices: Vec<Invoice>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Invoice {
pub number: String,
pub amount: f64,
pub paid: bool,
#[serde(rename = "paidAt")]
pub paid_at: Option<String>, // date-time
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Team {
pub id: String, // uuid
pub name: String,
pub website: String, // url
}
Typing APIs by hand?
I set up end-to-end type safety (DB → API → UI) so this never happens again.
How the Rust (serde) mapping works
Each JSON value is read, merged with every other sample of the same key, and then mapped to the closest Rust (serde) type:
| In your JSON | Rust (serde) |
|---|---|
| string | String |
| integer / decimal | i64 / f64 |
| true / false | bool |
| null alongside a value | Option<T> |
| key missing in some samples | Option<T> + #[serde(default)] |
| camelCase or invalid key | snake_case + #[serde(rename = ...)] |
| keyword key like type | r#type |
| array | Vec<T> |
| mixed values or always null | serde_json::Value |
Worked example
A user record with a nested team, a list of invoices (one missing a note, one with a null payment date) and an awkward display-name key:
{
"id": 1042,
"email": "ada@example.com",
"display-name": "Ada",
"createdAt": "2026-11-07T09:30:00Z",
"plan": null,
"tags": ["beta", "founder"],
"team": {
"id": "7f3c9a2e-1b4d-4c8e-9f2a-6d5e4c3b2a19",
"name": "Analytical Engines",
"website": "https://example.com"
},
"invoices": [
{ "number": "INV-001", "amount": 49.5, "paid": true, "paidAt": "2026-10-01T00:00:00Z" },
{ "number": "INV-002", "amount": 49, "paid": false, "paidAt": null, "note": "Card declined" }
]
}Becomes this Rust (serde), generated by the same engine that runs the converter above (3 types, 16 fields):
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
pub id: i64,
pub email: String, // email
#[serde(rename = "display-name")]
pub display_name: String,
#[serde(rename = "createdAt")]
pub created_at: String, // date-time
pub plan: serde_json::Value,
pub tags: Vec<String>,
pub team: Team,
pub invoices: Vec<Invoice>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Invoice {
pub number: String,
pub amount: f64,
pub paid: bool,
#[serde(rename = "paidAt")]
pub paid_at: Option<String>, // date-time
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Team {
pub id: String, // uuid
pub name: String,
pub website: String, // url
}
Rust (serde) gotchas
Missing vs null
Option<T> accepts null, but a missing key also needs #[serde(default)]. The generated attributes include both where the sample shows it.
Timestamps stay strings
Parsing dates needs the chrono or time crate. Fields are String with a comment; swap in chrono::DateTime<Utc> with serde support if you use it.
serde_json::Value is an escape hatch
Mixed-type fields compile, but you lose type safety. Consider an #[serde(untagged)] enum once you know the shapes.
Questions developers actually ask
Which crates do I need?
Why r#type instead of type_?
Can I deny unknown fields?
Is my JSON sent to a server?
How does it know a field is optional?
Other languages
More from the workbench
Fine print
Tools: on the house.
Engineer: by the hour.
A codebase that doesn't need this tool is the upgrade. I set up end-to-end type safety so your API, database and UI agree by construction.
I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.
