Skip to content

JSON to Rust Structs

Generates serde-annotated Rust structs from a JSON sample, with Option for nullable or missing fields and snake_case renames.

Emits Rust structs with `serde` derives from a sample JSON document — `Serialize`, `Deserialize`, the right `Option<…>` on fields that were sometimes absent, and a `#[serde(rename = "…")]` wherever the JSON key is not a legal snake_case field name.

🔒 Runs entirely in your browserrustjsoncodegenserde
Emit

Sample JSON

Drop a .json file

Read in this tab. Nothing is uploaded.

Give it every element you have. Every extra record narrows the guess — a field missing from one of them is what makes it optional.

Rust

How to use it

  1. Paste as many records as you have

    Every element of every array is read and merged, not just the first. Three records tell it far more than one, because a field missing from one of them is exactly what makes that field optional.

  2. Name the root type

    Nested objects are named after the key that holds them, singularised for arrays, so `users` yields a `User`. Only the root needs a name from you.

  3. Read the counts under the output

    Types, optional fields, unions and how many records were merged. A union count above zero means a field held two different kinds of value across your sample — worth looking at before you paste the result into a codebase.

  4. Paste the structs into your crate

    The `use serde::{Deserialize, Serialize};` line is emitted for you; the crate itself and its `derive` feature are yours to add to Cargo.toml.

Common questions

Why did a field come out as `serde_json::Value`?

Because it held more than one kind of value across the sample — a string in one record and a number in another. Rust has no untagged union to fall back on here, and `Value` is the honest type: it deserialises without lying about what is inside.

What is the difference between `Option<T>` and `#[serde(default)]` here?

A field that was `null` in some records becomes `Option<T>`; a field that was simply absent also gets `#[serde(default)]`, so deserialising a record without it succeeds instead of erroring. Both appear together when the sample showed both.

Are enums generated for string fields?

No. Every string is `String`. Inferring an enum from a handful of records means a deserialise error the first time production sends a variant your sample did not contain, which is a worse failure than a loose type.

Can I derive other traits?

Add them yourself — the emitted derive list is deliberately just `Serialize, Deserialize`. `Debug` and `Clone` are almost always wanted too, but they are a one-line edit and guessing wrong makes the output not compile for types that cannot derive them.

Feed it from

Next

These accept what this tool produces, so the result can go straight in.