DevToolBoxGRATIS
Blogg

JSON til C#-klasse: System.Text.Json, Newtonsoft & Records Guide

17 min lesningby DevToolBox

Converting JSON to C# classes is an essential task in modern .NET development. Whether you are building an ASP.NET Core Web API, a Unity game, a Blazor WebAssembly app, or a MAUI mobile client, you need strongly typed C# classes to deserialize JSON responses from REST APIs and configuration files. A reliable JSON to C# class converter eliminates hours of manual coding and reduces runtime errors. This comprehensive guide covers type mapping, System.Text.Json and Newtonsoft.Json strategies, record types, POCO generation, nested structures, .NET 8 source generators, and best practices for generating a C# class from JSON. If you need to convert JSON to C# online right now, try our free tool.

Try our free online JSON to C# Class Converter instantly.

What Is JSON to C# Class Conversion?

JSON (JavaScript Object Notation) is the universal data interchange format for web APIs, cloud services, and configuration systems. C# is a statically typed language that requires explicit class definitions before data can be consumed programmatically. JSON to C# class conversion bridges this gap by analyzing a JSON document and producing corresponding C# classes with properly typed properties, nullable annotations, and serialization attributes.

In a typical ASP.NET Core application, a controller receives an HTTP request body as a JSON string. The framework must convert JSON to C# objects using either the built-in System.Text.Json serializer or the popular Newtonsoft.Json library. Without properly defined classes, C# JSON deserialize operations fail at runtime with cryptic exceptions. A JSON to C# class converter automates the creation of these data transfer objects so you can focus on business logic.

The same conversion is critical in Unity game development where JSON configs define levels, items, and player data, in Blazor apps that consume third-party APIs, and in Azure Functions processing event payloads. Whether you call it JSON to C# class, JSON to POCO, or generate C# class from JSON, the underlying process is identical: inspect the JSON structure, determine each property's type, handle nesting and arrays, and produce clean, annotated C# source code.

JSON to C#: Type Mapping

Understanding how JSON types map to C# types is the foundation of any <strong>JSON to C#</strong> conversion. The following table shows the standard mappings used by <strong>System.Text.Json</strong>, <strong>Newtonsoft.Json</strong>, and most code generators:

JSON TypeExampleC# Type(s)Notes
string"hello"stringAlways maps to System.String
number (integer)42int, longUse long for values exceeding 2^31-1; use int? when nullable
number (decimal)3.14double, decimalUse decimal for financial data to avoid floating-point errors
booleantrueboolUse bool? when the field can be absent or null
nullnullnullNullable reference types (string?) or nullable value types (int?)
array[1, 2, 3]List<T>, T[]List<T> preferred; IReadOnlyList<T> for immutability
object{"k": "v"}Nested class or Dictionary<string, object>Strongly typed nested classes preferred over dictionaries

When you generate a C# class from JSON, choosing between value types (int, bool) and their nullable counterparts (int?, bool?) matters. With C# nullable reference types enabled (#nullable enable), properties that may be absent in the JSON should be declared nullable. For monetary values, always prefer decimal over double to avoid precision loss. Arrays of objects should map to List<NestedClass> to leverage generics and LINQ.

How JSON to C# Conversion Works

A JSON to C# class converter follows a systematic process to transform raw JSON into compilable C# source code:

  1. Parse the JSON structure: The converter tokenizes the input JSON, building an abstract syntax tree of objects, arrays, and primitives. Invalid JSON is rejected at this stage.
  2. Infer property types: For each key-value pair, the converter determines the C# type. Strings become string, integers become int or long, decimals become double or decimal, booleans become bool, and nested objects become new classes.
  3. Generate class and property names: JSON keys like user_name or shipping-info are converted to PascalCase property names (UserName, ShippingInfo) following C# naming conventions.
  4. Handle nested objects: Each nested JSON object generates a separate C# class. Deeply nested structures produce a class hierarchy. Circular references are detected and handled.
  5. Handle arrays: JSON arrays are analyzed to determine the element type. Arrays of objects produce List<ElementClass>. Mixed-type arrays fall back to List<object>.
  6. Add serialization attributes: The converter optionally adds [JsonPropertyName] (System.Text.Json) or [JsonProperty] (Newtonsoft) attributes when the JSON key does not match C# naming conventions.
  7. Output source code: The final step emits properly formatted C# source with using directives, namespace declarations, property declarations, and optional nullable annotations.

Code Examples: JSON to C# with System.Text.Json and Newtonsoft

System.Text.Json (.NET 8+): JsonSerializer and Attributes

System.Text.Json is the built-in, high-performance JSON serializer in .NET. Starting with .NET 8, it supports source generators for AOT compilation, making it the recommended choice for new projects. Here is a complete example showing how to convert JSON to C# objects with JsonSerializer, [JsonPropertyName], custom converters, and source generators:

// === Sample JSON ===
// {
//   "user_id": 1001,
//   "user_name": "Alice",
//   "email": "alice@example.com",
//   "is_active": true,
//   "balance": 1250.75,
//   "tags": ["admin", "developer"],
//   "address": {
//     "street": "123 Main St",
//     "city": "Springfield",
//     "zip_code": "62704"
//   }
// }

// === C# class with System.Text.Json attributes ===
using System.Text.Json;
using System.Text.Json.Serialization;

public class User
{
    [JsonPropertyName("user_id")]
    public long UserId { get; set; }

    [JsonPropertyName("user_name")]
    public string UserName { get; set; } = "";

    public string Email { get; set; } = "";

    [JsonPropertyName("is_active")]
    public bool IsActive { get; set; }

    public decimal Balance { get; set; }

    public List<string> Tags { get; set; } = new();

    public Address Address { get; set; } = new();
}

public class Address
{
    public string Street { get; set; } = "";
    public string City { get; set; } = "";

    [JsonPropertyName("zip_code")]
    public string ZipCode { get; set; } = "";
}

// === Deserialization with JsonSerializer ===
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

// Single object
User? user = JsonSerializer.Deserialize<User>(jsonString, options);

// List of objects
List<User>? users = JsonSerializer.Deserialize<List<User>>(
    jsonArrayString, options);

// === Custom JsonConverter for special cases ===
public class EpochToDateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(
        ref Utf8JsonReader reader, Type typeToConvert,
        JsonSerializerOptions options)
    {
        return DateTimeOffset.FromUnixTimeSeconds(
            reader.GetInt64()).DateTime;
    }

    public override void Write(
        Utf8JsonWriter writer, DateTime value,
        JsonSerializerOptions options)
    {
        writer.WriteNumberValue(
            new DateTimeOffset(value).ToUnixTimeSeconds());
    }
}

// Usage: [JsonConverter(typeof(EpochToDateTimeConverter))]
// public DateTime CreatedAt { get; set; }

// === .NET 8 Source Generator (AOT-friendly) ===
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(List<User>))]
public partial class AppJsonContext : JsonSerializerContext { }

// Zero-reflection deserialization:
User? u = JsonSerializer.Deserialize(
    jsonString, AppJsonContext.Default.User);

Newtonsoft.Json: JsonConvert and Attributes

Newtonsoft.Json (Json.NET) has been the de facto standard for C# JSON deserialize operations for over a decade. It offers a richer feature set including dynamic parsing with JObject, LINQ-to-JSON, and extensive customization through JsonConverter:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;

public class User
{
    [JsonProperty("user_id")]
    public long UserId { get; set; }

    [JsonProperty("user_name")]
    public string UserName { get; set; } = "";

    public string Email { get; set; } = "";

    [JsonProperty("is_active")]
    public bool IsActive { get; set; }

    public decimal Balance { get; set; }

    public List<string> Tags { get; set; } = new();

    public Address Address { get; set; } = new();
}

// === Deserialization with JsonConvert ===
var settings = new JsonSerializerSettings
{
    MissingMemberHandling = MissingMemberHandling.Ignore,
    NullValueHandling = NullValueHandling.Ignore,
    DateFormatString = "yyyy-MM-ddTHH:mm:ssZ"
};

// Single object
User? user = JsonConvert.DeserializeObject<User>(
    jsonString, settings);

// List of objects
List<User>? users = JsonConvert.DeserializeObject<List<User>>(
    jsonArrayString, settings);

// === Dynamic parsing with JObject ===
JObject obj = JObject.Parse(jsonString);
string? name = (string?)obj["user_name"];
JArray? tags = (JArray?)obj["tags"];
int tagCount = tags?.Count ?? 0;

// LINQ-to-JSON queries
var activeUsers = JArray.Parse(jsonArrayString)
    .Where(u => (bool)u["is_active"]!)
    .Select(u => (string?)u["user_name"])
    .ToList();

// === Custom JsonConverter ===
public class BoolToIntConverter : JsonConverter<bool>
{
    public override bool ReadJson(
        JsonReader reader, Type objectType, bool existingValue,
        bool hasExistingValue, JsonSerializer serializer)
    {
        return Convert.ToInt32(reader.Value) == 1;
    }

    public override void WriteJson(
        JsonWriter writer, bool value,
        JsonSerializer serializer)
    {
        writer.WriteValue(value ? 1 : 0);
    }
}

// Usage: [JsonConverter(typeof(BoolToIntConverter))]
// public bool IsActive { get; set; }

C# Records: Immutable Data Models

C# 9+ records provide a concise way to define immutable data models for JSON to C# class conversion. Records automatically generate Equals, GetHashCode, ToString, and support with expressions for non-destructive mutation:

// C# Record classes for JSON deserialization (C# 9+)
using System.Text.Json.Serialization;

public record User(
    [property: JsonPropertyName("user_id")] long UserId,
    [property: JsonPropertyName("user_name")] string UserName,
    string Email,
    [property: JsonPropertyName("is_active")] bool IsActive,
    decimal Balance,
    List<string> Tags,
    Address Address
);

public record Address(
    string Street,
    string City,
    [property: JsonPropertyName("zip_code")] string ZipCode
);

// Deserialization works seamlessly with records
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
User? user = JsonSerializer.Deserialize<User>(json, options);

// Records are immutable: use "with" for modified copies
User updated = user! with { Email = "new@example.com" };

// Init-only record class (C# 10+)
public record class Product
{
    public required long Id { get; init; }
    public required string Name { get; init; }
    public required decimal Price { get; init; }
    public bool InStock { get; init; }
    public List<string> Categories { get; init; } = new();
}

// Value-based equality: two records with same data are equal
var p1 = new Product { Id = 1, Name = "Keyboard", Price = 79.99m };
var p2 = new Product { Id = 1, Name = "Keyboard", Price = 79.99m };
Console.WriteLine(p1 == p2);  // True

Manual POCO: Class with Properties

When you need full control over your JSON to C# classes, a traditional POCO (Plain Old CLR Object) with explicit properties, INotifyPropertyChanged for data binding, or integration with AutoMapper provides maximum flexibility:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;

// POCO with INotifyPropertyChanged for WPF/MAUI data binding
public class Product : INotifyPropertyChanged
{
    private long _id;
    private string _name = "";
    private decimal _price;
    private bool _inStock;
    private List<string> _categories = new();

    [JsonPropertyName("product_id")]
    public long Id
    {
        get => _id;
        set => SetField(ref _id, value);
    }

    public string Name
    {
        get => _name;
        set => SetField(ref _name, value);
    }

    public decimal Price
    {
        get => _price;
        set => SetField(ref _price, value);
    }

    [JsonPropertyName("in_stock")]
    public bool InStock
    {
        get => _inStock;
        set => SetField(ref _inStock, value);
    }

    public List<string> Categories
    {
        get => _categories;
        set => SetField(ref _categories, value);
    }

    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged(
        [CallerMemberName] string? name = null)
    {
        PropertyChanged?.Invoke(
            this, new PropertyChangedEventArgs(name));
    }

    private bool SetField<T>(
        ref T field, T value,
        [CallerMemberName] string? name = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;
        field = value;
        OnPropertyChanged(name);
        return true;
    }

    // Override ToString for debugging
    public override string ToString()
        => $"Product {{ Id={Id}, Name={Name}, Price={Price} }}";
}

// Usage with AutoMapper (DTO to domain model):
// var config = new MapperConfiguration(cfg =>
//     cfg.CreateMap<ProductDto, Product>());
// var mapper = config.CreateMapper();
// Product product = mapper.Map<Product>(dto);

Working with Nested JSON Structures

Real-world APIs rarely return flat JSON. Most responses contain deeply nested objects, arrays of objects, and polymorphic types. Converting these complex structures into a C# class from JSON requires careful design:

Deeply nested objects: Each level of nesting produces a separate C# class. For a JSON structure like {"user": {"address": {"city": "NYC"}}}, you need three classes: the root class, a User class, and an Address class. In C#, these can be separate files, nested classes, or records defined in the same file.

Arrays of objects: A JSON field like "items": [{"id": 1}, {"id": 2}] maps to List<Item> in C#. Both System.Text.Json and Newtonsoft handle generic list deserialization automatically. For immutable collections, use IReadOnlyList<Item>.

Polymorphic deserialization: When a JSON field can hold different object shapes based on a discriminator, .NET 7+ supports [JsonDerivedType] and [JsonPolymorphic] attributes for type-safe deserialization with System.Text.Json. Newtonsoft uses TypeNameHandling or custom JsonConverter implementations:

// Polymorphic deserialization with .NET 7+ System.Text.Json
using System.Text.Json.Serialization;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(EmailNotification), "email")]
[JsonDerivedType(typeof(SmsNotification), "sms")]
[JsonDerivedType(typeof(PushNotification), "push")]
public abstract class Notification
{
    public string Type { get; set; } = "";
    public string Message { get; set; } = "";
    public DateTime CreatedAt { get; set; }
}

public class EmailNotification : Notification
{
    public string Recipient { get; set; } = "";
    public string Subject { get; set; } = "";
}

public class SmsNotification : Notification
{
    public string PhoneNumber { get; set; } = "";
}

public class PushNotification : Notification
{
    public string DeviceToken { get; set; } = "";
    public string Title { get; set; } = "";
}

// JSON input:
// {"type":"email","message":"Hello","recipient":"a@b.com","subject":"Hi"}
// Automatically deserializes to EmailNotification

// Nested classes with arrays example
public class ApiResponse
{
    public bool Success { get; set; }
    public UserData Data { get; set; } = new();
    public List<ErrorDetail> Errors { get; set; } = new();
}

public class UserData
{
    public User User { get; set; } = new();
    public List<Order> Orders { get; set; } = new();
    public Address BillingAddress { get; set; } = new();
    public Address ShippingAddress { get; set; } = new();
}

public class Order
{
    public long OrderId { get; set; }
    public decimal Total { get; set; }
    public List<OrderItem> Items { get; set; } = new();
}

public class OrderItem
{
    public long ProductId { get; set; }
    public string Name { get; set; } = "";
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

Advanced Patterns: Source Generators, AOT, and Nullable Reference Types

.NET 8 source generators for System.Text.Json produce compile-time serialization code, eliminating reflection and enabling Native AOT deployment. This is critical for cloud-native microservices, serverless functions, and mobile apps where startup time matters:

// .NET 8 Source Generator for AOT-friendly serialization
using System.Text.Json;
using System.Text.Json.Serialization;

public record Product(
    [property: JsonPropertyName("product_id")] long ProductId,
    string Name,
    decimal Price,
    [property: JsonPropertyName("in_stock")] bool InStock,
    List<string> Tags,
    [property: JsonPropertyName("created_at")] DateTime CreatedAt
);

// Source generator context
[JsonSerializable(typeof(Product))]
[JsonSerializable(typeof(List<Product>))]
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    GenerationMode = JsonSourceGenerationMode.Default)]
public partial class AppJsonContext : JsonSerializerContext { }

// Zero-reflection serialization (AOT-compatible)
var product = JsonSerializer.Deserialize(
    json, AppJsonContext.Default.Product);
var jsonOut = JsonSerializer.Serialize(
    product, AppJsonContext.Default.Product);

// Works with ASP.NET Core minimal APIs
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain
        .Insert(0, AppJsonContext.Default);
});

Nullable reference types (NRT) in C# 8+ provide compile-time null safety for your JSON to C# class definitions. When enabled with #nullable enable, the compiler warns about potential null dereferences. Combined with required properties (C# 11+), you can enforce that mandatory JSON fields are always present:

// Nullable reference types + required properties (C# 11+)
#nullable enable

public class UserProfile
{
    public required string Name { get; init; }
    public required string Email { get; init; }
    public string? Nickname { get; init; }     // optional
    public int Age { get; init; }
    public string? AvatarUrl { get; init; }     // optional
    public required List<string> Roles { get; init; }
}

// Deserialization enforces required properties
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
var user = JsonSerializer.Deserialize<UserProfile>(json, options)
    ?? throw new InvalidOperationException(
        "Failed to deserialize user profile");

// Compiler warning if you forget to set required properties:
// var profile = new UserProfile { Name = "Alice" };
// Error CS9035: Required member 'Email' must be set

// JsonRequired attribute for runtime enforcement
public class StrictModel
{
    [JsonRequired]
    public string Id { get; set; } = "";

    [JsonRequired]
    public string Name { get; set; } = "";

    public string? Description { get; set; }
}

System.Text.Json vs Newtonsoft.Json comparison: System.Text.Json is 2-5x faster for most workloads, uses less memory, and supports source generators for AOT. Newtonsoft.Json offers richer features including LINQ-to-JSON, JObject dynamic access, better support for legacy serialization patterns, and more lenient parsing. For new .NET 8+ projects, System.Text.Json is recommended; for complex migration scenarios or when you need JObject-style dynamic parsing, Newtonsoft remains valuable.

Best Practices for JSON to C# Conversion

Follow these best practices when you convert JSON to C# classes to build robust, maintainable .NET applications:

Use PascalCase properties with serialization attributes: C# properties should be PascalCase. Use [JsonPropertyName("snake_case")] (System.Text.Json) or [JsonProperty("snake_case")] (Newtonsoft) to map JSON keys. Alternatively, set JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase globally.

Enable nullable reference types: Add #nullable enable to your project and mark optional JSON properties as nullable (string?, int?). This catches potential null reference exceptions at compile time rather than runtime.

Handle unknown properties gracefully: System.Text.Json ignores unknown properties by default. Newtonsoft throws by default, so set MissingMemberHandling = MissingMemberHandling.Ignore or use [JsonExtensionData] to capture unknown fields in a Dictionary<string, JsonElement>.

Use decimal for monetary values: Never use float or double for money. JSON number 19.99 can lose precision as a floating-point type. Map financial fields to decimal for exact arithmetic.

Prefer records for DTOs: Use C# records with init-only properties for data transfer objects. Records provide value-based equality, immutability, and concise syntax. Use with expressions when you need modified copies.

Use source generators for performance: In .NET 8+ projects, use [JsonSerializable] source generators to eliminate reflection overhead. This is required for Native AOT and significantly improves cold start times in serverless environments.

Validate deserialized data: Apply data annotations ([Required], [Range], [StringLength]) or FluentValidation rules to validate deserialized objects. Never trust that incoming JSON matches your expected schema without validation.

Related tools you might find useful: JSON to Java for Spring Boot and Android development, JSON to TypeScript for frontend interfaces, and JSON to Kotlin for Kotlin/Android projects.

JSON to JavaJSON to TypeScriptJSON to Kotlin

Frequently Asked Questions

System.Text.Json vs Newtonsoft.Json: which should I use for JSON to C#?

For new .NET 8+ projects, System.Text.Json is recommended. It is built into the framework, offers 2-5x better performance, lower memory allocation, and supports source generators for Native AOT compilation. Newtonsoft.Json (Json.NET) remains valuable for legacy projects, complex scenarios requiring JObject/LINQ-to-JSON dynamic parsing, or when you need features like TypeNameHandling for polymorphic serialization. Both libraries support records, custom converters, and nullable reference types. If you are starting a new ASP.NET Core project, System.Text.Json is the default and should be your first choice.

Should I use C# records or traditional classes for JSON deserialization?

Use C# records when you need immutable data transfer objects with minimal boilerplate. Records (C# 9+) automatically generate Equals, GetHashCode, ToString, and support with expressions for non-destructive mutation. Both System.Text.Json and Newtonsoft support record deserialization. Use traditional classes when you need mutability, INotifyPropertyChanged for UI data binding, complex initialization logic, or EF Core entity support. For ASP.NET Core API DTOs and configuration models, records with init-only properties are the modern best practice.

How do I handle unknown JSON properties that are not in my C# class?

System.Text.Json ignores unknown properties by default, so no configuration is needed. To capture unknown fields, add a Dictionary property with [JsonExtensionData]. In Newtonsoft.Json, unknown properties throw by default. Set MissingMemberHandling = MissingMemberHandling.Ignore on JsonSerializerSettings, or use [JsonExtensionData] with a Dictionary. This defensive approach ensures your application does not break when APIs add new fields.

Converting JSON to C# classes is a fundamental skill for every .NET developer. From simple POCOs to advanced patterns with records, source generators, nullable reference types, and polymorphic deserialization, the right approach depends on your project's requirements. Use our free online JSON to C# class converter for instant code generation, and refer to this guide for best practices on type mapping, nested structures, and choosing between System.Text.Json and Newtonsoft.Json.

Convert JSON to C# classes instantly with our free online tool.

𝕏 Twitterin LinkedIn
Var dette nyttig?

Hold deg oppdatert

Få ukentlige dev-tips og nye verktøy.

Ingen spam. Avslutt når som helst.

Try These Related Tools

C#JSON to C#JVJSON to Java ClassTSJSON to TypeScript{ }JSON Formatter

Related Articles

JSON til Java-klasse Konverterer: POJO, Jackson, Gson & Lombok Guide

Konverter JSON til Java-klasse online. Generer POJO med Jackson, Gson og Lombok med kodeeksempler.

JSON til TypeScript: Komplett guide med eksempler

Lær hvordan du konverterer JSON-data til TypeScript-interfaces automatisk. Dekker nestede objekter, arrays, valgfrie felt og beste praksis.

JSON til Kotlin Data Class: kotlinx.serialization, Moshi & Gson Guide

Konverter JSON til Kotlin data class online. Laer JSON-parsing med kotlinx.serialization, Moshi og Gson.