How to Convert JSON to C# Classes (System.Text.Json and Newtonsoft)
To deserialize JSON in .NET you need a class to bind it to, and writing those classes by hand is slow: every property needs the right type, and every property whose name differs from the JSON key needs an attribute or it silently stays null. Generating the classes from a real sample fixes both. This guide covers how the mapping works, the fastest way to do it, and the C#-specific gotchas around attributes, nullable types, and List.
The fastest route: a browser generator
For a one-off, paste the JSON into the JSON to C# converter. It reads the sample, infers the type of every property, and writes classes ready for System.Text.Json: nested objects become their own classes, arrays become List types, numbers resolve to long or double, and each property carries a [JsonPropertyName] attribute with the usings you need. Because it runs entirely in your browser, the JSON never leaves your machine, which matters when the sample is a real production response.
If the JSON will not convert, it is almost always invalid rather than genuinely unconvertible. Run it through the JSON parser to find the exact syntax error, or format it so you can see the structure first.
How JSON maps to C
A generator makes the same decisions every time. Knowing them tells you what to expect.
- Objects become classes. Each object turns into a class, and each key becomes an auto-property with a
[JsonPropertyName]attribute carrying the original key. - Nested objects get their own class. Rather than inlining a deep object, the generator names it and references it from the parent, which keeps the classes reusable.
- Arrays become
Listtypes.["a","b"]becomesList<string>; an array of objects becomesList<Item>with a generatedItemclass. - Numbers split into
longanddouble. Whole numbers map tolong; anything with a decimal point maps todouble.
Here is the shape of a simple conversion:
{ "id": 1, "name": "Ada", "tags": ["admin", "dev"] }
becomes:
using System.Collections.Generic;
using System.Text.Json.Serialization;
public class Root
{
[JsonPropertyName("id")]
public long Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("tags")]
public List<string> Tags { get; set; }
}
Why [JsonPropertyName] matters
System.Text.Json matches JSON keys to properties by name, and by default that match is case-insensitive but still name-for-name. When your property is UserId but the JSON key is user_id, the two will not connect unless you set a global naming policy or annotate the property:
[JsonPropertyName("user_id")]
public long UserId { get; set; }
Without that, UserId quietly stays at its default after deserialization, which is a common “my object is empty” bug in .NET. Generation removes it: every property gets the correct attribute, so the class binds no matter how the keys are named. If you prefer a global rule, JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } covers the camelCase case without per-property attributes.
Nullable value types
C# value types like long, double, and bool cannot be null, so they cannot represent a JSON value that is sometimes null or absent, you would get 0 or false instead, indistinguishable from a real value. When the sample shows a field as null, the generator makes that value type nullable:
[JsonPropertyName("discount")]
public double? Discount { get; set; }
Reference types (string, a class, a List) are already nullable, so they do not need the ?. Give the generator an array of several records, with nullable fields actually showing null, so it can mark the right ones nullable.
When you see object
A property that is only ever null, an empty array, or holds more than one unrelated type has no single concrete C# type, so it falls back to object (or List<object> for a mixed array). That is a lack of evidence, not a bug. Populate those fields in the sample to get precise types, and reach for the JSON viewer to confirm the shape of a deeply nested payload before you convert.
Deserializing in .NET
Once you have the classes, binding is one call. With System.Text.Json:
using System.Text.Json;
var order = JsonSerializer.Deserialize<Root>(json);
Two options are worth knowing. new JsonSerializerOptions { PropertyNameCaseInsensitive = true } relaxes key matching, and there is no “throw on unknown members” by default, so extra keys are ignored, which is convenient for third-party APIs that add fields over time. If your codebase is on Newtonsoft.Json instead, swap [JsonPropertyName] for [JsonProperty] and call JsonConvert.DeserializeObject<Root>(json).
The gotchas that bite
- Missing attributes. A property whose name does not match the key stays at its default without
[JsonPropertyName](or a naming policy). Generation gets this right; if you hand-edit, keep attributes in sync. - Value types swallow null. A
longturns a null or missing value into0. Use nullable value types (long?) when the distinction matters, which is what the generator does. - System.Text.Json vs Newtonsoft. The attribute names differ (
[JsonPropertyName]vs[JsonProperty]). Pick the one your project uses before pasting the classes in. - Large IDs as double. If any value in the sample forces a numeric property to
double, a large ID can lose precision. Have the API send big IDs as strings. - Case sensitivity.
System.Text.Jsonis case-insensitive by default in .NET’s defaults for ASP.NET, but not always elsewhere; setPropertyNameCaseInsensitiveif you rely on it.
Frequently asked questions
How do I convert JSON to a C# class?
Paste the JSON into the JSON to C# converter and it writes classes ready for System.Text.Json, inferring nested classes, List types, long/double, nullable value types, and [JsonPropertyName]. Then call JsonSerializer.Deserialize<Root>(json).
Why is my C# object empty after deserializing JSON?
Usually the property names do not match the JSON keys and there is no [JsonPropertyName] attribute or naming policy, so the binder cannot connect them. Generating the class gives every property the correct attribute.
Should I use long or long?
Use the nullable long? for a field that JSON may omit or send as null, because a plain long becomes 0 instead. The generator makes value types nullable when the sample shows null.
System.Text.Json or Newtonsoft.Json?
System.Text.Json is the built-in, high-performance default in modern .NET and is what this tool targets. Newtonsoft.Json is still common in older codebases; swap the attribute to [JsonProperty] and use JsonConvert if that is what you are on.
For the format underneath all this, see the guide on what JSON is, the same idea for Java in converting JSON to Java, and the roundup of free JSON tools for the rest of the toolkit.
Leave a Reply