FormatterHQ

JSON to Rust (Serde) Struct Generator

Generate Rust structs that derive serde’s Serialize and Deserialize, with Option for optional fields, Vec for arrays and serde renames for awkward keys.

Input JSON
Output RUST
Ready.

How to use the JSON to Rust

  1. Paste your JSON sample (an object, or an array of objects) into the Input editor, or use Sample, Upload file or Load URL, or press Sample to try one.
  2. Set the Top-level class name, for example Task; it is converted to PascalCase. Nested types are named after their property keys.
  3. The Types only option does not change Rust output: structs always derive serde traits and come with a short usage comment.
  4. Press Generate (it also re-runs automatically as you edit), then use Copy or Download.

JSON to Rust: serde-ready structs

In Rust, serde and serde_json turn JSON into strongly typed structs, but you have to declare those structs first. This JSON to Rust generator writes them from a sample. Each struct derives Debug, Clone, Serialize and Deserialize, uses pub fields in snake_case, and adds #[serde(rename = "...")] wherever a JSON key cannot be used as a Rust identifier.

Example

{
  "id": 42,
  "title": "Write docs",
  "done": false,
  "priority": 2.5,
  "tags": ["docs"],
  "owner": { "name": "Ada", "email": null },
  "due": "2026-10-01T09:00:00Z",
  "subtasks": [
    { "title": "Outline", "estimate": 2 },
    { "title": "Draft" }
  ]
}
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
    pub id: i64,
    pub title: String,
    pub done: bool,
    pub priority: f64,
    pub tags: Vec<String>,
    pub owner: Owner,
    pub due: String,
    pub subtasks: Vec<Subtask>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Owner {
    pub name: String,
    pub email: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subtask {
    pub title: String,
    pub estimate: Option<i64>,
}

The optional estimate is Option<i64>; serde treats a missing key as None for Option fields. email, which was only null, is Option<serde_json::Value> so any JSON value is accepted. The output above is shown with blank lines between fields removed.

Using the structs

// Cargo.toml
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"

let task: Task = serde_json::from_str(&json)?;
println!("{} has {} subtasks", task.title, task.subtasks.len());
let back = serde_json::to_string_pretty(&task)?;

The generated file starts with a commented example; the extern crate lines in it date from Rust 2015 and are not needed in the 2018 or 2021 editions.

Reading serde errors

When the JSON does not match the structs, serde_json returns an error with the location, such as missing field `title` at line 1 column 42 or invalid type: string "3", expected i64. The first means a non-Option field was absent: make it Option<T> or add #[serde(default)]. The second means the API sends a different type than your sample showed; change the field type or use serde_json::Value while you investigate.

Naming and renames

  • camelCase keys become snake_case fields with a rename attribute; you can replace many renames with one #[serde(rename_all = "camelCase")] on the struct.
  • Keys that are Rust keywords, such as type, get a prefixed field name (root_type) plus a rename.
  • Keys starting with a digit, such as 2fa, become names like the_2_fa.

Type mapping and adjustments

JSONRust
integer / decimali64 / f64
string (including dates)String
array / objectVec<T> / a struct
optional or nullableOption<T>
null only / mixedserde_json::Value or an enum
  • Dates stay String. For real date types, enable the serde feature of chrono or time and change the field to DateTime<Utc> or OffsetDateTime.
  • Smaller integers: use u32 or i32 if you know the range; serde reports an error if a value does not fit.
  • Serializing None: add #[serde(skip_serializing_if = "Option::is_none")] to omit null fields in output.
  • Strictness: unknown keys are ignored by default; add #[serde(deny_unknown_fields)] to reject them.
  • Tests: add PartialEq to the derive list so you can compare decoded values with assert_eq! in unit tests.

Structs for XML input: XML to Rust. Validate your sample with the JSON Validator, and generate a language-neutral contract with JSON to JSON Schema. Everything runs in your browser.

Frequently asked questions

Which crates do I need for the generated Rust code?
serde with the derive feature and serde_json. The structs derive Serialize and Deserialize, so serde_json::from_str and to_string work directly.
Why are dates String instead of chrono types?
The generator keeps dates as strings so no extra crate is required. Switch to chrono or time types with their serde features if you need date arithmetic.
Why do some fields have #[serde(rename)]?
Rust field names must be valid snake_case identifiers. When a JSON key is camelCase, a keyword or starts with a digit, the rename attribute maps the Rust name back to the JSON key.
Does the Types only option change the output?
No. Rust output always consists of serde-derived structs, which are already the idiomatic way to parse JSON.
How do I reject unexpected fields?
Add #[serde(deny_unknown_fields)] to a struct. By default serde ignores keys it does not know.

Last updated September 26, 2026

Related tools