Convert JSON to C# classes or records

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.

C# code that reads JSON with System.Text.Json needs types whose properties match the keys, and JsonPropertyName maps a PascalCase property to the original key. This page generates them from a sample, as positional records (C# 9 and later) or as plain classes with get and set accessors. A nested object becomes its own type, a list becomes List of that type, whole numbers are long and numbers with a fraction are double. A key that is missing from some items, or null in some, becomes a nullable type such as string? and, in a record, a parameter with a default of null placed after the required ones, as C# requires. An RFC 3339 timestamp becomes DateTime; a plain date stays string, so the models work on any .NET version that has System.Text.Json. A property that would share the name of its type gets a Value suffix, because C# does not allow that. Mixed or unknown values become object.

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

Record or class?

A record is short and immutable, good for data you only read. A class has get and set accessors and works with older C# versions. Pick whichever your project uses.

Which JSON library does it target?

System.Text.Json, with JsonPropertyName attributes. For Newtonsoft.Json replace the attribute with JsonProperty.

Why is a plain date a string?

DateOnly needs .NET 7 or newer for built-in System.Text.Json support, so plain dates such as 2024-05-01 stay string. Timestamps with seconds and a Z or offset map to DateTime. Turn off Detect timestamps to get string for those too.

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.