JSON to C# Classes Converter

Paste JSON to generate C# classes, records, or annotated models. 100% client-side.

Last reviewed: April 2026

New to this tool? Click here for instructions

JSON Input
C# Output
Paste JSON above to generate C# classes.

Paste a JSON sample and instantly generate strongly-typed C# classes or records with correct nullable annotations, JsonPropertyName attributes, and inferred numeric and date types — ready to drop into a Visual Studio or Rider project.

What This Tool Does

This tool reads a JSON sample — typically an API response, a configuration file, or a message payload — and emits a complete set of C# type definitions that round-trip cleanly with that JSON. The output is not a single class. For nested objects it recursively builds a separate type per level, names them in PascalCase based on the parent property, and links them together with strongly-typed properties or positional record parameters. Arrays of objects become List<T> where T is itself a generated type, so a deeply nested API payload turns into a tidy file you can paste into a single Models.cs and use immediately.

Three output modes cover the common cases. Class mode emits mutable POCOs with auto-properties — the right choice for EF Core entities, ASP.NET Core controllers that bind from form posts, and any code where downstream consumers expect to mutate the deserialized object. Record mode emits C# 9+ positional records, giving you value-based equality, concise syntax, and a free with expression for non-destructive copies — the right choice for immutable DTOs and message-bus payloads. JsonProperty mode adds Newtonsoft.Json's [JsonProperty("originalKey")] attribute on every property so JSON keys like order_id or customerName map cleanly to PascalCase OrderId and CustomerName properties without needing global naming policies. System.Text.Json users can substitute [JsonPropertyName] with no other code changes.

Nullable reference types — enabled by default in new C# 8+ projects — are honored throughout the output. When a property's JSON value is null in the sample, the corresponding C# type is annotated with ?. When the value is present, the type is non-nullable. This is not a perfect signal (a single sample cannot prove a field is always present), but it gets you 80% of the correct annotations on the first paste, leaving only obviously optional fields to adjust by hand. Everything runs locally in your browser — your JSON payload is never uploaded, stored, or logged. 🔒

How to Use It

The flow is paste, pick, copy — usually under 30 seconds for a typical API response.

1. Paste Your JSON

Drop your JSON sample into the input pane on the left. The tool accepts any valid JSON: an object at the root, an array of objects (the first element is used as the schema), nested objects to any depth, and mixed arrays. As you type or paste, the output regenerates after a 150-millisecond debounce, so editing the sample to add or remove fields shows the schema change immediately on the right.

2. Pick a Mode

Use the three chips at the top to choose between Class, Record, or JsonProperty output. Class is the default and most familiar. Record gives you C# 9+ immutability — the entire type definition collapses to a single public record Customer(string Name, string Email); line. JsonProperty adds Newtonsoft.Json key-mapping attributes for cases where JSON keys do not match valid or idiomatic C# identifiers.

3. Pick Your Target C# Version

The exact output adjusts based on what your project supports. C# 8 introduced nullable reference types — these are emitted as string? on properties whose JSON value was null. C# 9 introduced records — the Record mode requires C# 9 or later (.NET 5+). C# 10 introduced global usings and the file-scoped namespace syntax; the output uses neither, so it stays portable. C# 11 added the required modifier (public required string Name { get; set; }), which you can apply manually after pasting if your API contract guarantees a field's presence. C# 12 added primary constructors on classes — if you prefer that style, use Record mode and convert the resulting record to a class with the same parameter list.

4. Copy or Download

The Copy button puts the generated code on your clipboard so you can paste it directly into a .cs file. Download saves it as output.cs. Both buttons preserve the using directives at the top of the output (using System.Collections.Generic; and, for JsonProperty mode, using Newtonsoft.Json;). Drop the file into your project's Models/ folder, rename the root class if you want something more descriptive than RootObject, and you are ready to call JsonSerializer.Deserialize<RootObject>(json).

Worked Example: Order Payload from a REST API

Suppose you are integrating with an order-management API and the first sample response you have is the JSON below. The goal is a strongly-typed C# model that you can deserialize into and pass through your domain layer.

Input JSON
{"orderId":12345,"customer":{"name":"Alice","email":"a@b.com"},"items":[{"sku":"X-1","qty":2,"price":19.99}],"placedAt":"2025-01-15T10:30:00Z"}
Mode
Record (C# 9+ positional records) — the order is conceptually immutable once placed, so value-based equality is desirable.
  1. Paste the JSON. The tool detects four distinct types: the root Order, the nested Customer, the array element Item, and the array itself as List<Item>.
  2. Inferred types per field. orderId becomes int because 12345 fits comfortably in int range. customer recursively generates a nested Customer record. items becomes List<Item>. The item's price field becomes decimal because the value 19.99 has a fractional component and matches the heuristic for currency. placedAt is detected as ISO 8601 and becomes DateTime — you should manually upgrade this to DateTimeOffset for production code (more on this below).
  3. Generated output (Record mode).
    public record Order(int OrderId, Customer Customer, List<Item> Items, DateTime PlacedAt);
    public record Customer(string Name, string Email);
    public record Item(string Sku, int Qty, decimal Price);
  4. Adjust int vs long. The sample orderId is 12345, which fits in int. But if your real order IDs come from a Snowflake generator or a database with a bigint column, change int OrderId to long OrderId before merging. Sample-based inference cannot tell you the actual numeric range — it can only tell you the smallest type that fits the one example you pasted.
  5. Adjust DateTime vs DateTimeOffset. The placedAt field has a Z suffix indicating UTC. Change DateTime PlacedAt to DateTimeOffset PlacedAt so the explicit timezone offset survives the round trip. As a rule of thumb: if the JSON has a Z, a +HH:MM, or a -HH:MM suffix, use DateTimeOffset. If it is a naive yyyy-MM-ddTHH:mm:ss with no zone info, DateTime with explicit DateTimeKind.Utc handling is acceptable.
  6. Deserialize. With System.Text.Json: var order = JsonSerializer.Deserialize<Order>(json, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); — the naming policy maps orderId in the JSON to OrderId in the record. Or, with attributes from JsonProperty mode, the policy is unnecessary because each property carries an explicit name binding.

Type-inference summary for this payload: int (orderId, qty), decimal (price), string (name, email, sku), DateTime → upgrade to DateTimeOffset (placedAt), nested record (Customer), list of records (List<Item>). The tool gets four of six type decisions right on the first pass; the two that need manual review are the integer width (int vs. long) and the date type (DateTime vs. DateTimeOffset).

Common Use Cases

The tool earns its keep in a handful of recurring scenarios — most of which involve a new contract appearing in front of you and a deadline measured in minutes.

Onboarding to a New REST API

You have an API endpoint and a sample response. You need a typed model in your codebase, not JObject indexers scattered through your business logic. Paste the sample, choose Class mode if you bind into ASP.NET MVC views (parameterless constructors required) or Record mode if you treat the response as an immutable DTO. You have a working model in under a minute and can spend the rest of the hour writing actual business logic instead of typing property declarations by hand.

Generating DTOs from Sample Responses

In service-oriented architectures, every service-to-service call needs a DTO on both sides. Producing those DTOs manually for every endpoint of every service is the kind of toil that quietly consumes engineering hours with no upside. Generating them from sample payloads — especially when the producing service is owned by another team and only the response example is documented — turns a one-hour task into a five-minute task.

gRPC-to-REST and REST-to-gRPC Migration

When converting an internal gRPC service to a public REST API (or vice versa), you have .proto definitions on one side and JSON contracts on the other. Generating C# classes from a sample JSON response gives you a translation layer reference for the gRPC types you need to define — or for the JSON contracts you need to specify if you are going the other direction. The generated C# model is a useful intermediate artifact even if it never lives in production code.

ServiceStack and Refit Scaffolding

Client SDK frameworks like ServiceStack and Refit accept typed request and response models. Paste the upstream JSON, generate the C# record, and the interface method signature drops in directly: [Get("/api/orders/{id}")] Task<Order> GetOrderAsync(int id);. The generated record is the response type — no further work required for the happy path.

Replacing Dynamic and JObject with Typed Access

Code that uses dynamic or JObject indexers (obj["customer"]["email"].ToString()) is a maintenance liability — typos and shape changes are caught only at runtime. Generating typed models from a sample of the JSON you are currently accessing dynamically gives you a clear migration path: replace the dynamic call sites one at a time with property-accessor equivalents, and the compiler now catches every shape mistake.

Edge Cases and Type-Inference Pitfalls

Sample-based generation is fundamentally a heuristic. A single sample cannot prove a field is always present, always non-null, or always within a particular numeric range. Knowing where the heuristics break down lets you spot the manual fixes quickly.

Null Values in the Sample

When a JSON field's value is null, the tool cannot infer the underlying type from the sample alone. It emits object? as a safe default. If you know the field is a nullable string, change object? MiddleName to string? MiddleName by hand. The broader lesson: when generating from a sample, fill in obvious null fields with representative values before pasting (a real middle name like "Marie") so the tool can infer the correct type, then revert the value in the generated code if needed.

Empty Arrays

An empty array "tags": [] gives the tool no element type to work with. The default emission is List<object>. Replace this with the actual element type (List<string>, List<Tag>, etc.) once you know what the array contains. Avoid List<dynamic> in production code — it propagates the type erasure through your codebase.

Heterogeneous Arrays

An array with mixed types ([1, "two", true]) is a JSON anti-pattern but it appears in real payloads — particularly from older XML-translated APIs and some event-streaming feeds. The tool defaults to List<object>. Better long-term fixes: ask the producer to split the array into named fields, define a discriminated union with a base class plus a JsonConverter that dispatches on a "type" field, or use the polymorphic serialization attributes introduced in System.Text.Json 7.

ISO 8601 → DateTime vs. DateTimeOffset

The tool currently emits DateTime for ISO 8601 strings. In practice, DateTimeOffset is almost always the right choice — it preserves the explicit timezone offset that DateTime silently drops. The only case where DateTime is genuinely correct is naive wall-clock values with no associated timezone (some legacy log timestamps, for example). For date-only values, C# 10's DateOnly type serializes as yyyy-MM-dd and is preferable to a midnight DateTime. Time-of-day values without a date should use TimeOnly.

Numeric Precision: decimal vs. double vs. int vs. long

JSON has one numeric type. C# has eight. The tool's heuristic is: integers fit in int if their absolute value is below 2,147,483,647; otherwise long. Numbers with a fractional component become double by default, except where the fractional pattern matches typical currency (two decimal places, common monetary magnitudes) — those become decimal. Always verify the inferred type for any field that holds money: decimal avoids the cumulative rounding errors that plague double for currency arithmetic. For scientific values, double is correct and faster.

C# Reserved Keywords as Property Names

If a JSON key collides with a C# keyword — class, namespace, event, using, default, operator — the generated code escapes it with the @ prefix: public string @class { get; set; }. With JsonProperty attributes the underlying JSON key is preserved, so deserialization continues to work. Rename to a non-conflicting PascalCase property if you prefer (CssClass, for example) and keep the JsonProperty attribute pointing at the original key.

PascalCase Conversion from camelCase and snake_case

JSON keys come in three common casings: camelCase (orderId), PascalCase (OrderId), and snake_case (order_id). C# convention is PascalCase for public properties. The tool converts all three to PascalCase in the property name and, when JsonProperty mode is active, preserves the original key in the attribute. If you do not use JsonProperty mode, configure JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase globally — that handles standard camelCase but not snake_case or kebab-case. For those, attributes are unavoidable.

Behind the Scenes: System.Text.Json, Newtonsoft.Json, and Source Generators

System.Text.Json vs. Newtonsoft.Json

For new projects on .NET 5 and later, System.Text.Json is the default JSON library — included in the BCL, no NuGet reference required, designed for high throughput. It is strict by default (rejects trailing commas, comments, and case-mismatched property names unless you opt in), uses Utf8JsonReader for zero-copy parsing of UTF-8 byte streams, and is about 1.3–2x faster than Newtonsoft.Json on typical workloads. Newtonsoft.Json remains the right choice for projects that need its specific feature set: JObject-based dynamic access, contract resolvers, custom converters with the maturity of a decade of community usage, and broader JSON-spec leniency (defaults to accepting comments and trailing commas).

JsonSerializerOptions and Round-Trip Configuration

System.Text.Json's behavior is configured through JsonSerializerOptions. The most common settings: PropertyNamingPolicy = JsonNamingPolicy.CamelCase for camelCase JSON without per-property attributes; WriteIndented = true for human-readable output; DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull to omit null properties on serialization; and Converters for custom logic on specific types. Construct one shared JsonSerializerOptions instance and reuse it — the cache it builds on first use is what makes it fast.

Source Generators for AOT

.NET 6 introduced the System.Text.Json source generator, which moves the reflection-heavy parts of (de)serialization to compile time. Mark a partial class with [JsonSerializable(typeof(Order))] and the generator emits a strongly-typed JsonSerializerContext at build time. This is required for trimmed and AOT-compiled apps (Native AOT, single-file publishing, Blazor WebAssembly with trimming enabled) because reflection-based serializers cannot be statically analyzed. The generated code is also faster than reflection at runtime — typically 30–40% improvement on cold paths. The generated C# classes from this tool work as-is with the source generator; just add the [JsonSerializable] attribute on a context class.

Record vs. Class Semantics

Records and classes are both reference types, but they differ in three important ways. Equality: records compare by value (every field is compared); classes compare by reference (object identity). ToString(): records emit a printable representation listing all properties; classes emit the type name. With-expressions: records support non-destructive copies (customer with { Email = "new@example.com" }); classes do not. Pick records when you want immutability, value semantics, and concise syntax. Pick classes when you need mutability, parameterless constructors (required by ASP.NET model binding in some scenarios), inheritance hierarchies with virtual members, or EF Core change tracking.

Comparison: This Tool vs. quicktype.io vs. json2csharp.com vs. Visual Studio "Paste JSON as Classes" vs. NSwag

The C# class generation space has several established players. Each makes different trade-offs between ease of use, language support, schema awareness, and integration depth.

JSON-to-C# Generation Tools Compared
Tool Input C# Modes Multi-Language Offline / Privacy Schema-Aware Best For
This tool JSON sample Class, Record, JsonProperty C# only (sister tools for TS/Go/Python/Java) 100% client-side, no upload No Fast paste-and-go workflow with explicit mode toggles
quicktype.io JSON, JSON Schema, GraphQL, TypeScript Class with attributes; optional records via CLI 30+ languages (C#, TS, Go, Rust, Kotlin, Swift, etc.) Web UI uploads; CLI is offline Yes (JSON Schema, GraphQL) Cross-language code generation; multi-sample inference
json2csharp.com JSON sample, URL Class with optional attributes C# only Web-based; sends JSON to server No Simple class output with attribute toggles
Visual Studio "Paste JSON as Classes" JSON sample on clipboard Class only (no records, no attributes) C# only 100% local (IDE feature) No IDE-integrated workflow; no browser context switch
NSwag CodeGen OpenAPI / Swagger spec Class with full attribute set, validation, async client methods C#, TypeScript CLI runs locally Yes (OpenAPI 2.0/3.0) Production SDK generation from authoritative API specs
NJsonSchema CSharpGenerator JSON Schema document Class with validation attributes C# only NuGet library, runs in your code Yes (JSON Schema Draft 4-7) Programmatic generation pipelines
Sample-based tools (this tool, quicktype, json2csharp, Visual Studio) are fast but imprecise. Schema-based tools (NSwag, NJsonSchema) are slower to set up but produce more accurate output once you have an authoritative schema.

A practical heuristic: start with a sample-based tool when exploring a new API or prototyping. Once the contract is stable and a schema is published, switch to a schema-based tool for the SDK that ships to production. Visual Studio's Paste JSON as Classes command (Edit → Paste Special → Paste JSON as Classes) is the lowest-friction option if you live in the IDE and never leave it; this tool wins when you want explicit mode toggles, records support, or are working without Visual Studio installed.

Frequently Asked Questions

Should I use records or classes when deserializing JSON in C#?

Use records when the deserialized data is treated as an immutable value (DTOs returned from a one-shot API call, message-bus payloads, configuration snapshots). Records give you value-based equality, concise positional syntax, and a free with expression for non-destructive copies. Use classes when consumers need to mutate properties after deserialization, when you rely on EF Core change tracking, when you need parameterless constructors for older frameworks, or when inheritance hierarchies are involved. System.Text.Json supports both since .NET 5; Newtonsoft.Json supports records from version 13.0.1 onward.

What is the difference between DateTime and DateTimeOffset in C#?

DateTime stores a point in time plus a DateTimeKind flag (Utc, Local, or Unspecified), but the flag is easily lost during serialization, equality comparison, or cross-process boundaries. DateTimeOffset stores the same instant plus an explicit offset from UTC, making it unambiguous regardless of where the value is later read. For ISO 8601 strings containing a Z or numeric offset (most modern APIs), DateTimeOffset is almost always the right choice. Use DateTime only for naive wall-clock values that have no associated timezone — log timestamps in a single-timezone system, for example.

Why does the tool generate decimal instead of double for some numbers?

decimal is a base-10 fixed-point type with 28-29 significant digits and exact representation of decimal fractions; double is base-2 floating point and cannot exactly represent values like 0.1 or 19.99. For monetary values and any data where rounding errors are unacceptable, decimal is correct. double is appropriate for scientific and statistical values where a 15-17 digit binary mantissa is sufficient and performance matters more than exactness. The tool defaults to decimal when a JSON number has a fractional component matching a likely currency pattern; you can override this in the output before pasting it into your project.

How do I handle nullable reference types in C# 8 and later?

Enable the feature in your project file with <Nullable>enable</Nullable> (per-project) or via #nullable enable / #nullable disable pragmas (per-file). Once enabled, a non-annotated reference type is treated as non-nullable: assigning null produces a compiler warning, and accessing a null value through a non-nullable reference is flagged. Append ? to make a reference type nullable: string? Name. For value types, ? has meant nullable since C# 2.0 (int? = Nullable<int>) and is unchanged. The tool emits ? on every property whose JSON value was null in the sample input.

Can I automatically add JsonPropertyName attributes for camelCase JSON?

Yes — switch the output mode to JsonProperty (Newtonsoft.Json) or substitute [JsonPropertyName] for System.Text.Json. The tool inserts the attribute above each property so the original JSON casing is preserved on the wire while your C# code follows PascalCase convention. An alternative without attributes is to configure JsonSerializerOptions with PropertyNamingPolicy = JsonNamingPolicy.CamelCase once globally; that handles the common case but breaks down for fields with unusual casing or for round-tripping payloads where you do not control the schema. Attributes are explicit and survive refactoring tools that rename properties.

How should I handle JSON arrays that contain values of different types?

Heterogeneous arrays are a JSON anti-pattern but they appear in real APIs. The tool emits List<object> as a safe default, which preserves the data but loses type information. Better long-term fixes: (1) ask the API owner to split the array into named fields, (2) introduce a discriminated union using a base class and JsonConverter that dispatches on a "type" property, or (3) for System.Text.Json 7+, use the new polymorphic serialization attributes [JsonPolymorphic] and [JsonDerivedType]. Newtonsoft.Json offers the same pattern via the JsonSubtypes NuGet package. Avoid dynamic in production code — it defers errors to runtime.

How do I round-trip dates correctly between JSON and C#?

Three rules cover almost every case. First, always serialize as ISO 8601 with a timezone designator (either Z for UTC or a numeric offset like +01:00) — JsonSerializerOptions in System.Text.Json does this by default for DateTimeOffset. Second, deserialize into DateTimeOffset rather than DateTime so the offset survives the round trip. Third, if you must use DateTime, pin DateTimeKind to Utc immediately after deserialization and call ToUniversalTime() before serializing; otherwise the implicit Kind = Unspecified can silently shift your value by the server's timezone offset on the next round trip. For date-only values, C# 10's DateOnly type serialized as yyyy-MM-dd is preferable.

Can I generate C# classes from a JSON Schema instead of a JSON sample?

This tool generates from sample JSON because it is the most common starting point — you have an API response in front of you and want a typed model in minutes. JSON Schema-driven generation gives you more accurate output (required vs. optional fields, enum constraints, numeric ranges, regex patterns on strings) and is the better choice when an authoritative schema exists. For schema-driven workflows the leading tools are NSwag (especially for OpenAPI/Swagger schemas), NJsonSchema's CSharpGenerator class, and the dotnet-svcutil-like packages bundled with .NET Core. Use sample-based generation for prototyping and exploratory work; switch to schema-based generation once the API contract is stable and the schema is published.