json

FormatValidateConvert

JSON to Rust

JSON
Language
Structured output

Result· read-only

—Rust

The result appears here.

Generate

JSON to Rust: generate Serde structs from JSON.

Your document never leaves the browser.

Rust structs deriving serde, from a JSON sample — in your browser, never uploaded.

Paste a document and get Rust structs that serde_json reads and writes. The Cargo.toml lines the code needs are in a comment at the top, so the file compiles once it is dropped into a crate.

None

for an absent key and for a null, by default

untagged

an enum for a field whose values differ in type

0

bytes leave your machine

Derives, renames and Option

Each object becomes a pub struct deriving Debug, Clone, Serialize and Deserialize, with pub fields. snake_case keys stay as they are, since Rust uses the same convention; any other key gets a legal field name and a #[serde(rename)] holding the original, so gift-wrap is the field gift_wrap.

By default an absent key and a null both read as None. note is null in one line item, so it is Option<String>. gift-wrap is missing from one, so it is Option<bool> with default, which lets the key be left out, and skip_serializing_if, which leaves it out again when you write the struct back.

1

The sample, an order with two line items, pasted as the source

{
  "order_id": 1042,
  "placed_at": "2026-09-25T10:15:00Z",
  "paid": true,
  "customer": { "name": "Ada Lovelace", "email": "[email protected]" },
  "items": [
    { "sku": "PEN-01", "qty": 2, "price": 3.5, "note": null },
    { "sku": "INK-07", "qty": 1, "price": 12, "note": "Fragile", "gift-wrap": true }
  ]
}
2

The structs written for it

// Generated by myjsoneditor.com for serde and serde_json (Rust 2021).
// Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1"
// An absent key and a null both read as None. Turn on "Distinguish null from absent" to keep them apart.

use serde::{Deserialize, Serialize};

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

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Item {
  pub sku: String,
  pub qty: i64,
  pub price: f64,
  pub note: Option<String>,
  #[serde(rename = "gift-wrap", default, skip_serializing_if = "Option::is_none")]
  pub gift_wrap: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
  pub order_id: i64,
  pub placed_at: String,
  pub paid: bool,
  pub customer: Customer,
  pub items: Vec<Item>,
}

The one option

Distinguish null from absent (serde_with) is off by default. Turn it on for a PATCH-style API where sending null means clear the value and leaving the key out means keep it: a field that may be both absent and null becomes Option<Option<T>>, using serde_with’s double_option, and serde_with = "3" joins the Cargo.toml comment.

1

A note that is text, then null, then missing

[
  { "id": 1, "note": "Fragile" },
  { "id": 2, "note": null },
  { "id": 3 }
]
2

With Distinguish null from absent (serde_with) on

// Generated by myjsoneditor.com for serde and serde_json (Rust 2021).
// Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1", serde_with = "3"
// A key that may be absent or null is Option<Option<T>>: None when absent, Some(None) when null.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RootItem {
  pub id: i64,
  #[serde(default, skip_serializing_if = "Option::is_none", with = "::serde_with::rust::double_option")]
  pub note: Option<Option<String>>,
}

pub type Root = Vec<RootItem>;

Mixed values and big numbers

Nested structs are named from their keys, and a Vec of objects is named in the singular, so items is Vec<Item>. A field whose values differ in type becomes an enum marked #[serde(untagged)], with one variant per type that was seen, so serde tries each in turn. A key that is a Rust keyword is written as a raw identifier, so type is r#type.

An integer beyond i64 and u64 is typed serde_json::Number, and the header tells you to enable serde_json’s arbitrary_precision feature, without which the number is rounded. That feature is incompatible with untagged enums, so when it is needed the mixed fields fall back to serde_json::Value, and the header says that too.

1

A mixed list and a keyword

{ "code": [1, "A"], "type": "gift" }
2

An untagged enum, and r#type

// Generated by myjsoneditor.com for serde and serde_json (Rust 2021).
// Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1"
// An absent key and a null both read as None. Turn on "Distinguish null from absent" to keep them apart.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CodeValue {
  Integer(i64),
  String(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
  pub code: Vec<CodeValue>,
  pub r#type: String,
}
3

The mixed list beside a 20-digit number

{ "account": 12345678901234567890, "code": [1, "A"] }
4

serde_json::Number, and the list falls back to serde_json::Value

// Generated by myjsoneditor.com for serde and serde_json (Rust 2021).
// Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1"
// An absent key and a null both read as None. Turn on "Distinguish null from absent" to keep them apart.
// Enable serde_json's "arbitrary_precision" feature, or serde_json::Number rounds integers outside i64 and u64.
// Mixed-type fields are serde_json::Value here, because that feature breaks #[serde(untagged)] enums.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
  pub account: serde_json::Number, // past i64 — needs serde_json's arbitrary_precision feature
  pub code: Vec<serde_json::Value>, // elements: one of: i64, String
}

Your sample stays here

Inference and code generation both run inside this browser tab. The sample is never uploaded, stored on a server or logged, because the page has no server to send it to, and once loaded it carries on working with the connection off.

Read next:

FAQ

Frequently asked questions

Didn’t find your answer?Write to us on the contact page →
Which crates do I need?

serde with the derive feature, and serde_json. Both lines are in the header comment, ready for Cargo.toml.

Why is price an f64 and qty an i64?

price has a fraction in the sample and qty never does. Change qty to u32 or another integer type if you know its range.

How do I read the JSON with these structs?

serde_json::from_str::<Root>(&text), with your root type in place of Root. It returns a Result whose error gives the line and column that failed.

Keyboard shortcuts

Send feedback

Questions, bug reports and feature requests are all welcome. A bug report is easiest to act on with the shape of the document that caused it — never send anything confidential.

Email us

Contact page, in a new tab, so this page stays as it is.

Settings

Indent

The result is written with it — YAML and XML at 2 spaces when it is Tab.

Code text size
14 px

Both panes.

Wrap long lines

Load from a URL

Your browser fetches it directly — the request goes to that site, never to us.