Skip to content
{ }JSON Formatter

JSON to Rust Converter

Generate Rust structs with serde derives from a JSON sample — Option<T> for nullables, Vec<T> for arrays, rename attributes for wire names.

Input
Output
Paste input and press Generate Rust.

Serde structs from a sample

In Rust, JSON handling starts with a struct and #[derive(Serialize, Deserialize)] — and for a realistic API response, writing that struct tree by hand means a lot of careful Option and Vec decisions. The generator makes them from evidence: null in the sample → Option<T>, arrays → Vec<T>, each object shape → its own struct, wire names preserved via #[serde(rename = "…")] where they don't match Rust's snake_case.

Types you can trust the compiler with

Rust's payoff is that the compiler enforces what the struct claims — which cuts both ways: an over-narrow type from a thin sample means serde_json::from_str errors at runtime on real data. Feed a representative sample (several array entries, the null cases you actually see) and the generated i64/f64/Option decisions will hold up. Integers default to i64, decimals to f64 — narrow deliberately, not optimistically.

Drop-in usage

Add serde (with the derive feature) and serde_json to Cargo.toml — the JSON to TOML converter is right there if you're assembling the config — paste the structs, and let data: Root = serde_json::from_str(&body)?; completes the pipeline.

Frequently asked questions

Which serde attributes does the output use?

derive(Serialize, Deserialize) on every struct, plus serde(rename = "...") wherever the JSON key differs from the snake_case Rust field. No custom deserializers — plain declarative serde.

Why Option<T> on some fields?

Because the sample showed null (or the field missing in some array entries). Option is the honest type: unwrap consciously or pattern-match, instead of crashing on the first real-world payload.

Why i64 and f64 instead of smaller types?

JSON numbers are width-less, so the generator picks the types that cannot overflow on plausible data. Narrowing to u32/i32/f32 is your call where the domain guarantees it — the compiler will hold you to it.

What crates do I need?

serde = { version = "1", features = ["derive"] } and serde_json = "1". Nothing else — the generated code is dependency-minimal by design.