Quick answer
To deserialize JSON in C#, you need a record or class whose properties match the JSON keys. Paste a sample into Playxoft's free JSON to C# generator to get them: keys become PascalCase properties with JsonPropertyName attributes, whole numbers become int or long, dates become DateTimeOffset, and values that are null or missing become nullable. Then call JsonSerializer.Deserialize with your root type.
- Records or classes
- System.Text.Json or Newtonsoft
- JsonPropertyName attributes
- Nullable reference types
- C# 11 required members
- DateTimeOffset and Guid
- File-scoped namespace
- Download Models.cs
How to use the JSON to C#
- 1Paste JSON into JSON sample and set the Root type name.
- 2Choose Records or Classes, and a Serializer: System.Text.Json, Newtonsoft.Json or none.
- 3Enter a Namespace, pick a Collection type, and toggle Nullable references, required members, DateTimeOffset and Guid.
- 4Copy the code or download Models.cs into your project.
- 5Deserialize with
JsonSerializer.Deserialize<Root>(json), orJsonConvert.DeserializeObject<Root>(json)for Newtonsoft.
System.Text.Json or Newtonsoft.Json?
Both turn JSON into C# objects, and the generator writes the right attributes for either.
| System.Text.Json | Newtonsoft.Json (Json.NET) | |
|---|---|---|
| Ships with | .NET Core 3.0 and later | A NuGet package |
| Key attribute | [JsonPropertyName("key")] | [JsonProperty("key")] |
| Property name matching | Case-sensitive by default | Case-insensitive |
| Single quotes, unquoted keys | Rejected | Accepted |
| Native AOT and trimming | Yes, with source generation | No |
For new code, System.Text.Json is the default: it is built in, fast and actively developed. Keep Newtonsoft.Json where a codebase already depends on its flexibility, such as custom converters, JObject manipulation or loosely formatted input.
Records or classes for JSON models?
Records (C# 9 and later) are the modern choice for data transfer objects: one line per property, immutable, and compared by value, which makes them ideal for API payloads you read but don't mutate. System.Text.Json fills positional records through their constructor:
public record User(
[property: JsonPropertyName("id")] long Id,
[property: JsonPropertyName("email")] string? Email
);The property: target matters. Without it the attribute lands on the constructor parameter: System.Text.Json's [JsonPropertyName] isn't allowed there, so the code won't compile (CS0592), and Newtonsoft's [JsonProperty] would rename the key when reading but not when writing.
Classes with { get; set; } properties suit code that builds or edits objects field by field, older frameworks, and ORMs that need a parameterless constructor. Switch the generator to Classes and turn on required members to make missing keys fail loudly.
How are JSON types mapped to C#?
| JSON in your sample | C# type |
|---|---|
| Whole numbers that fit 32 bits | int |
| Larger whole numbers | long |
Whole numbers beyond long | decimal |
| Decimal numbers | double |
| ISO 8601 date-time strings | DateTimeOffset |
| UUID strings | Guid |
| Other strings | string |
| Arrays | List<T>, T[] or IReadOnlyList<T> |
| Objects | A record or class named after the key |
| Empty or ID-keyed objects | Dictionary<string, T> |
Mixed types, or only null | JsonElement (object with Newtonsoft) |
Switch double to decimal for money: double can't represent 0.1 exactly, while System.Text.Json reads a JSON number straight into decimal without that rounding. DateTimeOffset is preferred over DateTime because it keeps the UTC offset that ISO 8601 timestamps carry.
Do I need [JsonPropertyName] on every property?
Not if the API's naming is consistent. Set a naming policy once and drop the attributes:
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, // .NET 8+: user_id maps to UserId
};
var user = JsonSerializer.Deserialize<User>(json, options);
// Or the web defaults: camelCase names, case-insensitive matching
var web = new JsonSerializerOptions(JsonSerializerDefaults.Web);The generator writes explicit attributes anyway, because real APIs are rarely consistent: a userId, a created_at and a 2fa often share one payload. Attributes make every mapping visible and survive renaming a property in your IDE. Choose Serializer: None (plain) if you prefer bare types plus a policy.
Visual Studio's Edit > Paste Special > Paste JSON As Classes is a quick start, but its property names follow the keys as written and it adds no attributes, nullable annotations or records. This generator writes idiomatic PascalCase properties, maps each one back to its original key, and lets you choose records or classes.
How do nullable reference types and required work with JSON?
With #nullable enable, a string property promises never to be null. The generator marks a property nullable (string?, int?) whenever a sample had null or left the key out, so the compiler warns before you dereference it.
On classes, required members (C# 11) go further: System.Text.Json in .NET 7 and later throws a JsonException when a required property is missing from the input, turning a silent null into an early, clear error. For trimmed or Native AOT apps, add a source-generated context so no reflection is needed at runtime:
[JsonSerializable(typeof(User))]
internal partial class AppJsonContext : JsonSerializerContext { }
var user = JsonSerializer.Deserialize(json, AppJsonContext.Default.User);Frequently asked questions
Last updated by the Playxoft engineering team.