JSON to C# Classes — Generate .NET Model Classes Instantly
Paste JSON and get C# classes with properties, data annotations, and Newtonsoft or System.Text.Json attributes. Copy straight into your .NET project.
You are integrating a third-party payment API into your .NET application. The API docs show a sample JSON response for an order: customer information, line items, totals, shipping address. Before you can call JsonSerializer.Deserialize<T>, you need to build the C# class that T represents.
Doing it by hand means creating OrderResponse, CustomerDto, LineItem, and ShippingAddress classes — each with the right property names, the right types, and the right casing conventions. For a ten-endpoint API that might be four or five hours of boilerplate, and every typo in a property name is a silent null at runtime.
Use the JSON to Code generator to paste the JSON and get ready-to-use C# POCO classes in seconds.
What the generator produces
A JSON-to-C# generator reads the JSON structure and emits C# class definitions with public properties. It handles the three core conventions your .NET code expects:
- PascalCase properties — C# uses
OrderId, notorderId. The generator renames camelCase JSON keys to PascalCase C# properties. - Correct .NET types — JSON numbers become
intordouble, booleans becomebool, strings remainstring, arrays becomeList<T>. - Nested classes — JSON objects become separate C# classes, referenced from the parent.
A complete example
Take an order response from a REST API:
{
"orderId": "ORD-9821",
"total": 149.99,
"isPaid": true,
"customer": {
"id": 7,
"name": "Bob Smith"
},
"items": [
{ "sku": "WIDGET-A", "qty": 2, "price": 49.99 }
]
}
A generator produces:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public string Sku { get; set; }
public int Qty { get; set; }
public double Price { get; set; }
}
public class Order
{
public string OrderId { get; set; }
public double Total { get; set; }
public bool IsPaid { get; set; }
public Customer Customer { get; set; }
public List<Item> Items { get; set; }
}
The JSON key orderId became the C# property OrderId. The nested customer object became its own Customer class. The items array of objects became List<Item>. This is exactly what JsonSerializer.Deserialize<Order>() or JsonConvert.DeserializeObject<Order>() expects.
Newtonsoft.Json vs System.Text.Json
The two main JSON serializers in .NET use different attributes to map JSON key names to C# property names. This matters because the JSON is camelCase (orderId) and your C# properties are PascalCase (OrderId). Without mapping, the deserializer won’t find the value.
Newtonsoft.Json (also called Json.NET, the Newtonsoft.Json NuGet package — still dominant in many projects):
using Newtonsoft.Json;
public class Order
{
[JsonProperty("orderId")]
public string OrderId { get; set; }
[JsonProperty("total")]
public double Total { get; set; }
[JsonProperty("isPaid")]
public bool IsPaid { get; set; }
}
System.Text.Json (built into .NET 5+, the System.Text.Json namespace — preferred for new projects):
using System.Text.Json.Serialization;
public class Order
{
[JsonPropertyName("orderId")]
public string OrderId { get; set; }
[JsonPropertyName("total")]
public double Total { get; set; }
[JsonPropertyName("isPaid")]
public bool IsPaid { get; set; }
}
An alternative to per-property attributes is a global naming policy. With System.Text.Json you can configure camelCase deserialization once at the serializer level:
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var order = JsonSerializer.Deserialize<Order>(jsonString, options);
With this option enabled, orderId in the JSON matches OrderId in the class without any attributes. Good generators let you choose between emitting attributes on each property or relying on a naming policy.
Using the classes to deserialize
Once you have your classes, deserialization is one line:
// System.Text.Json (.NET 5+):
var order = JsonSerializer.Deserialize<Order>(jsonString);
Console.WriteLine(order.Customer.Name); // "Bob Smith"
Console.WriteLine(order.Items[0].Sku); // "WIDGET-A"
// Newtonsoft.Json:
var order = JsonConvert.DeserializeObject<Order>(jsonString);
Console.WriteLine(order.OrderId); // "ORD-9821"
For HTTP responses, System.Text.Json integrates directly with HttpClient:
var order = await httpClient.GetFromJsonAsync<Order>("/api/orders/9821");
This extension method (from System.Net.Http.Json) handles reading the response stream and deserializing in one call.
Making generated classes production-ready
The raw generator output gets you most of the way there. Before checking in, apply these adjustments:
Fix double to decimal for money
Generators infer double for any JSON number with a decimal point. For monetary values, that is wrong — floating-point arithmetic is not suitable for money. The price and total fields in the example should be decimal:
public decimal Total { get; set; }
public decimal Price { get; set; }
decimal has 28-digit precision and no floating-point rounding issues. Always use it for currency.
Add nullable reference types for .NET 6+
With nullable reference types enabled (the default in .NET 6+ projects), the compiler expects you to be explicit about which reference-type properties can be null. A generated string Name triggers a warning. Either initialize it or declare it nullable:
public string Name { get; set; } = string.Empty; // non-nullable with default
public string? MiddleName { get; set; } // explicitly nullable
Consider records for immutable API responses
If you only read API responses and never mutate them, C# 9+ record types are a cleaner fit than classes:
public record Customer(int Id, string Name);
public record Item(string Sku, int Qty, decimal Price);
public record Order(
string OrderId,
decimal Total,
bool IsPaid,
Customer Customer,
List<Item> Items
);
Records provide value-based equality, immutability, and a compact syntax. They work with both System.Text.Json and Newtonsoft.Json. The tradeoff is that positional records require constructor arguments in the right order, which can be brittle if the JSON structure changes frequently.
Add data annotations for validation
Generators don’t emit data annotations — you add those based on your domain rules. Common ones for API response classes that also get used as input models:
using System.ComponentModel.DataAnnotations;
public class Customer
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; } = string.Empty;
}
public class Item
{
[Required]
public string Sku { get; set; } = string.Empty;
[Range(1, int.MaxValue)]
public int Qty { get; set; }
[Range(0.01, double.MaxValue)]
public double Price { get; set; }
}
These annotations are evaluated by Validator.ValidateObject() and automatically enforced by ASP.NET Core model binding. Add them when the same class is used for both deserialization and input validation.
Workflow: from API docs to working C# code
Here is the end-to-end process:
-
Get a real sample response. Use Postman, Swagger UI, or
curlto call the endpoint and capture the full JSON response. A partial or example-from-docs response may be missing fields. -
Validate the JSON first. Paste it into the JSON Formatter to confirm it is well-formed. A trailing comma or missing bracket causes the generator to fail, and the error message may not point directly at the problem.
-
Generate the classes. Paste the validated JSON into the JSON to Code generator. Select C# and choose your serializer preference (Newtonsoft or System.Text.Json).
-
Review and edit. Fix
doubletodecimalfor money, add?nullable annotations, rename ambiguously named classes (Root→OrderResponse), add data annotations where needed. -
Drop into your project. Place the classes in a
Models/orDtos/folder. Wire up deserialization. -
Re-generate on API changes. When the third-party API adds fields or changes types, paste the new sample and compare against your existing classes.
What to watch for in generated output
A few patterns in generator output need human review:
Arrays of objects named Root or Item. Generators name classes based on the key. If the JSON has a key named data or list, the generated class will be named Data or List — which conflicts with System.Collections.Generic.List. Rename these immediately.
All-integer fields inferred as int. Large integer IDs (Twitter/X IDs, Unix timestamps in milliseconds) can overflow int. If an ID field looks like it could exceed 2,147,483,647, change it to long.
Missing optional fields. If a field is sometimes absent from the response, the generator will only know about it if your sample included it. Always read the API documentation alongside the generated output.
The generator handles the tedious part. You provide the domain knowledge about what the types actually mean.
Written by Mian Ali Khalid. Last updated 2026-05-12.
Related posts
- JSON to Go Struct — Generate Golang Types from a JSON Sample — Paste JSON and get Go struct definitions with json tags. Handles nested structs,…
- JSON to TypeScript Interface — Auto-Generate Types from Any JSON — Paste JSON and get TypeScript interfaces instantly. Auto-generates nested types,…
- JSON Formatter — Why Formatting JSON Matters and How It Works — A JSON formatter takes compact, hard-to-read JSON and adds whitespace and indent…
Written by Mian Ali Khalid. Part of the Data & Format pillar.