Convert JSON to Rust serde structs

Paste JSON and get typed models for Go, Python, C#, Java, Kotlin, Rust or Swift. List items are merged and numbers are typed from how they are written. Nothing is uploaded.

Rust reads JSON through serde, which needs a struct that derives Deserialize, with field names in snake_case. This page writes those structs from a sample: each nested object becomes its own struct, lists become Vec, whole numbers are i64 and numbers with a fraction are f64. A key missing from some items, or null in some, becomes Option with #[serde(default)] and skip_serializing_if, so a missing key does not fail parsing. Keys in camelCase or with dashes are converted to snake_case with a #[serde(rename = "...")] attribute that keeps the original key. Rust keywords such as type, match or struct are legal keys in JSON but not field names, so they get a trailing underscore and a rename. Values that vary in type or are always null become serde_json::Value, which needs the serde_json crate next to serde with the derive feature. Dates stay String so you do not need a date crate.

How it works

  1. Paste a JSON sample
  2. Pick the language and the root name
  3. Copy or download the code

What the tool generates

To turn JSON into typed code, paste an object or a list of objects, choose Go, Python, C#, Java, Kotlin, Rust or Swift, and copy the generated structs, models or classes. Every nested object becomes its own type, named from its key in PascalCase, so the key orders gives a type Order when it holds a list. The sample opens as Go: id becomes ID int64 with the tag json:"id", user_name becomes UserName, and the ISO timestamp created becomes time.Time. Go gets json tags, Python gets Pydantic v2 BaseModel classes or dataclasses, C# gets records or classes with System.Text.Json attributes, Java gets records or plain classes with optional Jackson @JsonProperty annotations, Kotlin gets data classes with optional kotlinx.serialization annotations, Rust gets serde structs with Serialize and Deserialize derives, and Swift gets Codable structs with CodingKeys where a name differs from the key.

How shapes and numbers are inferred

The tool reads every item of a list and merges their shapes, because one sample item rarely shows the whole truth. A key that is missing from some items is optional: a Go pointer (a slice stays a nil-able slice) with omitempty, a Python Optional with a None default, a C# or Kotlin nullable type, a Rust Option with serde default. A key whose value is null in some items is nullable but still required. If one field holds a number in one item and text in another, no single type fits, so it falls back to the language's any type with a comment saying the types were mixed. An empty list has no items to read, so its element type is also the any type. Numbers are typed from how they are written, not from their value: 7 is an integer, while 1.0 and 1e3 are floats, because a strict integer decoder rejects them. An integer outside the signed 64-bit range, such as 18446744073709551615, gets the language's big type (Go *big.Int, C# and Java BigInteger, Swift Decimal, Rust serde_json::Number) instead of being rounded through a double. A field that mixes such an integer with decimals gets a lossless decimal type (json.Number, BigDecimal, Decimal, JsonElement), never a double, and a Swift integer of more than 38 digits is typed String because Decimal would round it.

Names, keywords and dates

Field names follow each language's style: PascalCase in Go and C#, camelCase in Java, Kotlin and Swift, snake_case in Python and Rust. Go uses its initialisms, so id becomes ID and url becomes URL. A key that is a keyword in the language gets a safe name and the original key is kept: a trailing underscore for Rust (including gen, reserved since the 2024 edition), Python and Java, backticks for Kotlin and Swift. Go struct tags cannot name every legal JSON key: a key of - is written as the tag "-,", while a key with a comma, a quote or nothing at all cannot be a tag name, so that field is marked with a comment. Generated type names avoid the names the output depends on, such as None in Python or List in Java. Two keys that give the same name get a number, and a key that starts with a digit gets a prefix. With Detect timestamps on, only full RFC 3339 timestamps such as 2024-05-01T10:30:00Z (a real calendar day, seconds, and a Z or offset) become time types. Date-only values stay strings. Rust keeps String so you do not need a date crate.

Limits

The sample decides everything: a field that is always present in your sample is required in the output, so check optional fields against your real data or an API specification. The tool does not know enums, string formats other than RFC 3339 timestamps, or value ranges. It accepts up to 500,000 characters of strict JSON and nested data up to 60 levels, and pastes over 20,000 characters are converted in a background worker so the page stays responsive. The output targets Go 1.18 or newer (for any), Pydantic v2, C# 9 records, Java 16 records with Jackson 2.12 or newer, kotlinx.serialization, serde 1 and Swift 5 Codable. The work happens in your browser and the JSON is not uploaded or stored; only your option choices are remembered. For TypeScript use the JSON to TypeScript tool.

Frequently asked questions

Which crates do I need?

serde with the derive feature, and serde_json to parse. Add chrono yourself if you want real date types, since dates stay as String here.

How are Rust keywords handled?

A key such as type becomes the field type_ with #[serde(rename = "type")]. gen is reserved since the 2024 edition, so it becomes gen_ the same way. The raw identifier r#type also works, but self, super and crate cannot be raw, so the underscore is used everywhere.

Why is a field an Option?

The key is missing from some items or null in some. Option with serde default lets parsing succeed without it, and skip_serializing_if leaves it out when you write JSON again.

How do I convert JSON to a Go struct?

Paste the JSON, leave the language on Go and copy the result. A key id with a number becomes ID int64 with the tag json:"id", and a key missing from some list items becomes a pointer with omitempty (a list stays a slice, which is nil when the key is absent).

How does it decide which fields are optional?

A key is optional when it is missing from at least one object in a list, for example coupon in 1 of 2 orders. Keys that are present in every object, or in a single object sample, are required, even when the value is null in some.

How are numbers typed: 1 versus 1.0?

From the written form. 7 is an integer (int64, long, i64), while 1.0 and 1e3 are floats, and one float in a list makes the whole field a float. An integer beyond 9,223,372,036,854,775,807 gets a big type such as BigInteger.

Can I get Pydantic and dataclass output?

Yes. Choose Python and then Pydantic v2 or dataclass. Pydantic uses BaseModel and Field(alias=...) for keys like user-name, and a dataclass puts fields with defaults last because Python requires it.

Why is a field typed as any?

A field that is only null in the sample, an empty list, or a field that holds a number in one item and text in another gives no single type, so it becomes the any type (any, Any, object, JsonElement, serde_json::Value) with a comment. Add a sample item with a real value to fix it.

Is my JSON uploaded?

No. The code is generated in your browser, in this tab, and no server receives the JSON. Your option choices are saved in this browser; the JSON is not.