NV ToolkitFormat · Validate · Convert

Code Generators

JSON to Rust Converter

Paste a JSON or XML sample — the format is auto-detected — and get typed Rust models with names, nesting and nullability inferred for you.

In short

Convert JSON to Rust online: paste a sample and generate typed Rust models with serialization helpers — powered by quicktype, in your browser.

By NaveenKumar T · Updated

Learn: JSON Examples: From Hello World to Real API Payloads

  • JSON or XML input, auto-detected
  • Nested objects become types
  • Nullability inferred

Runs entirely in your browser — nothing you paste or open here is uploaded, logged, or stored.How we handle data →

Example

Example: JSON order payload to Rust

A small order document with a nested object, an array of two items, a decimal total and a null field — enough to show how each Rust type, list and nullable is inferred. The same page accepts the XML form of this order too, so the sample below is an example rather than the only accepted format. The right-hand side is the exact output of this tool.

Input · JSON
{
  "id": "ord_9F2KX",
  "status": "processing",
  "paid": true,
  "customer": { "name": "Ada Lovelace", "email": "ada@example.com", "phone": null },
  "items": [
    { "sku": "KB-75", "qty": 1, "price": 149.99 },
    { "sku": "MS-12", "qty": 2, "price": 39.5 }
  ],
  "total": 228.99
}
Generated · Rust
// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::Root;
//
// fn main() {
//     let json = r#"{"answer": 42}"#;
//     let model: Root = serde_json::from_str(&json).unwrap();
// }

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct Root {
    id: String,

    status: String,

    paid: bool,

    customer: Customer,

    items: Vec<Item>,

    total: f64,
}

#[derive(Serialize, Deserialize)]
pub struct Customer {
    name: String,

    email: String,

    phone: Option<serde_json::Value>,
}

#[derive(Serialize, Deserialize)]
pub struct Item {
    sku: String,

    qty: i64,

    price: f64,
}

Look for the "phone" field: sampled as null, it is typed as Option<serde_json::Value>. "items" became Vec<Item>, and "total" is f64.

Learn more

From JSON sample to Rust types

Hand-writing model classes from a JSON payload is transcription work: read a field, type a property, repeat eighty times, miss one. This page runs quicktype (the same engine behind the best-known generators) against your document, merging array elements to find the common shape and naming nested types from their keys.

serde ignores unknown fields by default, so a renamed key on the API side is not an error — the struct just fills in None or fails on a required field somewhere else. Add #[serde(deny_unknown_fields)] to the structs you want to fail fast, and add pub to the fields you need to read from other modules, since quicktype emits neither.

How JSON values become Rust types

Inference works from the values in your sample. Strings map to String, integers and decimals are told apart (228.99 becomes f64, while 1 and 2 stay integral), true/false become the language's boolean, and an object such as "customer" becomes its own named type. The "items" array is typed as Vec<Item> because every element shares the same keys — quicktype unifies the shapes of all array elements into one Item type.

Nulls are the one place inference needs your help: "phone": null carries no type information, so the field is emitted as Option<serde_json::Value>. If the real API sometimes sends a string there, include a sample where the field is populated (paste several documents merged into one array) and the generated type becomes the union of what it saw.

Pasting XML instead: how elements and attributes become Rust types

The XML is parsed into a data model before inference (attributes included, text nodes named "text"), so the root element becomes a nested type: <order> is a field of type Order on the top-level Root rather than the top level itself. Child elements become fields, and repeated elements — the two <item> siblings — are collected into Vec<Item> under an Items wrapper type that mirrors the <items> container.

Attributes and element text are typed differently, and this matters: element text is type-inferred, so <total>228.99</total> becomes f64, while attribute values are kept as they were written — qty="1" and price="149.99" stay String in Rust. Prefer element text for numeric data you want typed, or cast attribute values after decoding. One more trap: a single <item> would not be recognized as a list, so make sure repeated elements appear at least twice in the sample.

Annotations, nulls and nested arrays in the generated Rust

Structs derive Serialize and Deserialize and carry nothing else. Renames are emitted only where they are needed: a key like customer-ref gets a #[serde(rename)] attribute above a customer_ref field, while keys that are already valid snake_case identifiers get no attribute at all — which is why a tidy sample produces attribute-free structs. One detail to fix before you build: the fields are declared without pub, so they are private to the module the file lands in. Add pub to the fields you read from elsewhere in the crate.

A field sampled only as null becomes Option<serde_json::Value> — an option wrapping an untyped value, which is as much as inference can honestly say. Merge a document where the field is populated and it collapses into the union you want, Option<String>, where None covers both null and a missing key. Keys absent from some of the merged samples get the same Option treatment. An empty array is worse than a null: it becomes a Vec of optional untyped values, so seed arrays with one real element.

Every nested object becomes its own derived struct named from the key — Customer, Item — and arrays of objects unify into Vec<Item>, with quicktype merging the keys of every element into a single struct. Fields present in some elements but not others come back as Option<T>, which is exactly what you want from a real payload. That merge is the reason to give each array two or more elements: a one-element array produces a struct where every field looks mandatory, and an empty array produces no element struct at all.

Help

Frequently asked questions

Everything you need to know about the JSON to Rust.

01How do I generate Rust classes from JSON?

Paste a representative document — JSON or XML, the format is detected automatically — and press Convert. A real API response works best. Nested objects become their own Rust types, arrays become Vec<Item>, and the result downloads as Root.rs.

02What does the generated Rust code include?

Rust output uses serde: #[derive(Serialize, Deserialize)] structs with rename attributes for non-Rust field names and Option<T> for nullables — paste a payload, cargo add serde, done.

03How do I use the generated Rust code in my project?

Add serde (with the derive feature) and serde_json to Cargo.toml, place the file in your crate as a module, and call serde_json::from_str::<Root>(&json). Structs derive Serialize and Deserialize, and fields keep their wire names through #[serde(rename)] wherever Rust naming rules force a change.

04Which crates does the generated Rust need?

serde with derive plus serde_json — the universal pair. Field names re-case to snake_case with #[serde(rename)] preserving the wire format, so the JSON round-trips byte-faithfully.

Keep working

Related tools

Learn the format

Related guides

All guides →