In ASP.NET Core Web API, JSON serialization is the process of converting C# objects into JSON strings (for responses) and back again (for request bodies).
By default, ASP.NET Core handles this automatically, but understanding how to configure it is essential for building professional APIs.
1. The Default Serializer: System.Text.Json
Since .NET Core 3.0, Microsoft provides a high-performance, low-memory library called System.Text.Json. It is the built-in choice for modern applications and is optimized for speed and security.
How it works automatically
When you return an object from a controller, ASP.NET Core uses a "Formatter" to serialize it:
C#
[HttpGet]
public IActionResult GetUser()
{
var user = new User { Id = 1, Name = "Alice" };
return Ok(user); // Automatically serialized to {"id": 1, "name": "Alice"}
}
2. Configuration & Global Settings
You can customize serialization behavior globally in your Program.cs. Common tasks include enabling "pretty printing" (indentation) or changing how property names are cased.
C#
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
// 1. Indent the JSON for readability
options.JsonSerializerOptions.WriteIndented = true;
// 2. Ignore properties that are null
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
// 3. Keep property names as they are in C# (PascalCase) instead of camelCase
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
3. Using Attributes for Fine Control
Sometimes you only want to change serialization for a specific class or property. You can use attributes directly on your models:
| Attribute | Purpose |
[JsonPropertyName("user_id")] | Renames the property in the JSON output. |
[JsonIgnore] | Prevents the property from being serialized at all. |
[JsonInclude] | Forces a private property to be included. |
[JsonConstructor] | Tells the serializer which constructor to use for deserialization. |
Example:
C#
public class Product
{
[JsonPropertyName("sku_code")]
public string Sku { get; set; }
[JsonIgnore]
public decimal InternalCost { get; set; }
}
4. System.Text.Json vs. Newtonsoft. Json
While System.Text.Json is the default, many legacy or complex projects still use Newtonsoft.Json (Json.NET).
| Feature | System.Text.Json (Default) | Newtonsoft.Json (Json.NET) |
| Performance | Faster, uses less memory. | Slower due to heavy reflection. |
| Compatibility | Built into .NET. | Requires a NuGet package. |
| Features | Strict, standard-compliant. | Highly flexible, handles edge cases easily. |
| Native AOT | Fully supported (via Source Gen). | Not supported. |
How to switch to Newtonsoft.Json:
If you need specific features like JsonPatch or complex reference handling, install the Microsoft.AspNetCore.Mvc.NewtonsoftJson package and update Program.cs:
C#
builder.Services.AddControllers().AddNewtonsoftJson();
5. Best Practices for 2026
Stick to System.Text.Json: Unless you have a specific technical debt reason to use Newtonsoft, the performance gains and security of the built-in library are worth it.
Use Source Generation: For high-traffic APIs or Cloud Native/Native AOT apps, use JSON Source Generators to eliminate reflection at runtime.
Avoid Circular References: If your models reference each other (e.g., Parent → Child → Parent), use ReferenceHandler.IgnoreCycles to prevent the serializer from crashing.
Contracts First: Always define DTOs (Data Transfer Objects) specifically for your API rather than serializing your Database Entities directly.
Are you looking to migrate an existing project from Newtonsoft.Json, or are you starting a fresh project from scratch?