Browser processing Input is processed on your device. Privacy & storage

JSON to Rust

Generate serde structs from a JSON sample — derives, renames and Option included.

JSON input
Rust output

What the generator emits

Every object becomes a pub struct carrying #[derive(Debug, Clone, Serialize, Deserialize)]. Field names are converted to snake_case, and any key whose snake_case form differs keeps its wire spelling in a #[serde(rename = "…")] attribute — so the Rust you read stays idiomatic while the JSON stays byte-for-byte compatible.

JSON valueRust typeNotes
whole numberi64Signed 64-bit covers the range a JSON parser can hand you.
decimal numberf64—
true / falsebool—
stringStringOwned, because the payload outlives the parse call.
arrayVec<T>Element types are merged across all elements.
objecta named structHoisted to the top level.
null, or a key missing from a siblingOption<T>Both mean "this may not be there".
mixed or unknownserde_json::ValueImport added automatically.

Optional keys become Option<T>

Rust has no field-level "not present" state, so optionality has to come from Option. The generator marks a field Option<T> when the key was null, or when it appeared in one element of an array but not in its siblings:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Address {
    pub city: String,
    #[serde(rename = "zipCode")]
    pub zip_code: Option<String>,
}

Without Option, serde would reject a payload with the key missing. Turn on Skip serialising absent fields to also add #[serde(skip_serializing_if = "Option::is_none")], which keeps null out of your outgoing JSON instead of writing one explicitly.

Using the structs

use serde_json;

let payload: Root = serde_json::from_str(&body)?;
println!("{}", payload.address.city);

// Round-trip back to the wire format
let text = serde_json::to_string(&payload)?;

Your Cargo.toml needs serde with the derive feature, plus serde_json for Value:

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Edge cases worth knowing

  • Numbers are not narrowed. A JSON 1 becomes i64 even if it is obviously a count — narrow types are a decision the generator cannot make for you.
  • Mixed arrays such as [1, "two"] fall back to serde_json::Value, since Rust has no untagged union to describe them.
  • Empty arrays become Vec<serde_json::Value> — the sample simply contains no element to infer from.
  • Recursive types are not possible here. A JSON document is a tree, so the generated structs never refer back to themselves and always compile as-is.

FAQ

Do I need use serde::{Deserialize, Serialize}; as well as the derive?

Yes — the derive macro expands to paths under the serde crate, so the traits must be in scope. The generator emits that use line at the top of the file.

Why is the field Option<T> rather than #[serde(default)]?

They solve different problems. #[serde(default)] fills in a value when the key is absent; Option preserves the distinction between absent, null and present. For an API client, keeping that distinction is usually what you want.

Is my JSON uploaded?

No. Everything runs in this browser tab.

Can I change the root struct name?

Yes — edit Root name and every reference updates, including the rename attributes.