JSON to C# Classes
Generate C# classes or records from sample JSON, matched to System.Text.Json or Newtonsoft — entirely on this device.
The generated C# appears here as you type.
Press ? for shortcuts
Do it offline:$ npx quicktype --lang csharp --framework SystemTextJson --top-level Root sample.json
How to use it
- Paste a JSON response body or drop a .json file — the C# regenerates as you edit.
- One payload rarely shows every field: add another sample from the same endpoint and the shapes merge.
- Set the serializer first — it decides every attribute — then classes or records, nullable reference types, List<T> or T[], and the top-level type name.
- Copy the source, download it as a .cs file, or share a link that reopens these samples and options.
Paste sample JSON and read back compilable C#: one type per object shape, attributes pinning the exact wire keys, collections as List<T> or arrays. Feed several payloads from the same endpoint and any field missing somewhere turns nullable. Output targets System.Text.Json or Newtonsoft.Json, classes or records, nullable reference types on or off — and generation happens in this tab, with nothing posted anywhere.
Examples
{"first_name": "Ana"}namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Globalization;
public partial class Root
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
}
}
The wire key rides in the attribute while the property gets idiomatic PascalCase — first_name is still what goes over the network, FirstName is what your code reads.
{"user_id": 7, "is_admin": false}namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Root
{
[JsonProperty("user_id")]
public long UserId { get; set; }
[JsonProperty("is_admin")]
public bool IsAdmin { get; set; }
}
}
The Newtonsoft flavor swaps the using block and the attribute — [JsonProperty] in place of [JsonPropertyName] — while 7 maps to long and false to bool, exactly as under the default serializer.
{"first_name": "Ana"}
{"first_name": "Maya", "middle_name": "Rose"}namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Globalization;
public partial class Root
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("middle_name")]
public string? MiddleName { get; set; }
}
}
Two samples from one endpoint: middle_name is absent from the first, so its property turns nullable and is left out of re-serialized output while null — merging is how optional fields get detected.
{"$type": "user", "name": "Ana"}namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Globalization;
public partial class Root
{
[JsonPropertyName("$type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
Metadata keys survive: $ cannot open a C# identifier, so the property becomes Type while the attribute pins the original $type — round-trips keep the discriminator intact.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Switching serializers is the whole migration
Generate the same sample under each serializer and diff the two: only the using directives and the attribute names change. Types, property names and nesting are identical, so a Newtonsoft-era model ports to System.Text.Json by swapping attributes.
Records change the property contract, not the shape
Record output turns every { get; set; } into { get; init; }: values are fixed once deserialization constructs the object. Attributes stay where they were. A Unity [Serializable] preset is a planned V2 addition, not on this page today.
string? is a compiler promise, not a runtime guard
With nullable reference types on, the source targets C# 8 and optional fields read string?. Turning the option off targets C# 6 and drops the annotations — null still arrives at runtime either way; only the compile-time warnings disappear.
Money never guesses decimal
Whole numbers map to long and fractions to double. A field that looks like currency is not silently promoted to decimal — a wrong guess would corrupt totals downstream — so edit the type by hand where cents must be exact.
"19.99" in quotes is a string, and stays one
Number-shaped string values keep string properties, and a chip counts them. Under System.Text.Json, JsonNumberHandling.AllowReadingFromString accepts such values into numeric properties without touching the payload itself.
A representative sample beats the whole export
Shapes, not volume, drive generation — three varied records teach it as much as ten thousand. Past 2 MB combined, a warning suggests trimming; past 10 MB the tool refuses outright instead of truncating what you pasted.
Common errors
Unexpected '}' at line 1, column 16 — probable cause: trailing comma after the last property.- Generation only starts from strict JSON, and the diagnostic points at the failing character with a caret frame. Trailing commas are legal in C# initializers and modern JavaScript, which is how they sneak into hand-edited payloads — delete the comma and the classes appear.
Sample 2 of 3 is not valid JSON — fix or remove it.- Merging needs every sample to parse on its own. The banner names the failing tab, and the diagnostics above it carry line, column and probable cause — correct that sample or drop its tab and generation resumes.
Input is larger than the 10 MB limit for this tool.- Past the cap the whole paste is declined — nothing is silently trimmed to fit. Keep a handful of representative records instead, or run the quicktype command shown on this page over the complete file.
FAQ
Should I pick System.Text.Json or Newtonsoft.Json?
System.Text.Json ships inside modern .NET and is the default here; choose Newtonsoft.Json when a codebase already depends on it. The generated shapes are the same either way — attributes and using directives are the only difference, so moving later is mechanical.
Why did a property come out as string? with a JsonIgnore attribute?
Because at least one of your samples lacked that field, the generator cannot prove it always arrives: the type turns nullable, and the WhenWritingNull condition keeps the field out of serialized output while it holds no value.
Why are timestamps plain strings, and mixed arrays a union type?
Semantics are never guessed from example values. An ISO-looking string might be an opaque identifier, so it stays string until you promote it to DateTimeOffset yourself; an array mixing numbers and strings generates a small struct holding either branch, with converters — not a silent fall-back to object.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Regenerate the C# immediately, at any input size.
- ⌘/Ctrl + S
- Save the generated source as a .cs file (overrides the browser save-page dialog; the help overlay notes this).
- ⌘/Ctrl + C
- With nothing selected, copy the entire generated source; an active selection copies normally.
- ⌘/Ctrl + Shift + C
- Force-copy the entire source even while a selection exists.
- t
- Cycle the color theme (idle in text fields and during IME composition).
- ? or ⌘/Ctrl + /
- Open the keyboard help dialog listing these bindings.
- Esc
- Close the topmost dialog; your samples stay put.