Configure System.Text.Json source generation for AOT-compatible, reflection-free JSON serialization with compile-time type metadata.
When to Use
- •Preparing application for Native AOT compilation
- •Eliminating reflection-based serialization overhead
- •Improving startup performance by generating serialization code at compile time
- •Ensuring JSON serialization errors surface at compile time
Requirements
- •.NET 8 or higher for full options support
- •.NET 6+ for basic source generation
Steps
- •
Invoke polymorphic skill first:
- •Run
dotnet-json-polymorphicskill to ensure polymorphic types are configured - •This ensures
[JsonDerivedType]attributes are in place before generating context
- •Run
- •
Ask scope:
- •Ask user: "Apply to entire solution or specific project?"
- •Filter to ASP.NET Core projects (those with web SDK or API endpoints)
- •
Scan for API endpoint types:
- •Search for Minimal API patterns:
MapGet,MapPost,MapPut,MapDelete,MapPatch - •Extract request/response types from lambda parameters and return types
- •Search for Minimal API patterns:
- •
Scan for controller types:
- •Search for controller attributes:
[HttpGet],[HttpPost],[HttpPut],[HttpDelete],[HttpPatch] - •Extract parameter types and return types from action methods
- •Search for controller attributes:
- •
Find direct serialization calls:
- •Search for
JsonSerializer.SerializeandJsonSerializer.Deserializecalls - •Extract type arguments from these calls
- •Search for
- •
Check for existing JsonSerializerContext:
- •Search for classes inheriting from
JsonSerializerContext - •Note existing
[JsonSerializable]types to avoid duplicates
- •Search for classes inheriting from
- •
Ask about enum serialization:
- •Ask user: "Enable enum-as-string converter? (Recommended: Yes)"
- •
Ask about property naming:
- •Ask user: "Select property naming policy:"
- •CamelCase (Recommended)
- •SnakeCaseLower
- •SnakeCaseUpper
- •KebabCaseLower
- •KebabCaseUpper
- •None (preserve original casing)
- •Ask user: "Select property naming policy:"
- •
Ask about null value handling:
- •Ask user: "Serialize null values? (Recommended: No)"
- •No - Omit null properties from output (smaller payloads)
- •Yes - Include null properties in output
- •If No: Add
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
- •Ask user: "Serialize null values? (Recommended: No)"
- •
Create JsonSerializerContext:
- •File:
{ProjectName}JsonContext.csin project root (same level as Program.cs) - •Class name:
{ProjectName}JsonContext
- •File:
- •
Add source generation options:
csharp[JsonSourceGenerationOptions( PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSerializable(typeof(WeatherForecast))] [JsonSerializable(typeof(List<WeatherForecast>))] public partial class MyApiJsonContext : JsonSerializerContext { } - •
Include collection types:
- •For each type
T, also add[JsonSerializable(typeof(List<T>))] - •Add
[JsonSerializable(typeof(T[]))]if arrays are used
- •For each type
- •
Configure ASP.NET Core:
- •Add to Program.cs:
csharpbuilder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolver = MyApiJsonContext.Default; }); - •
Verify with build:
bashdotnet build
- •
Report results:
- •List the JsonSerializerContext file created
- •List all types added to [JsonSerializable]
- •Confirm ASP.NET Core integration added
- •Confirm build status
Example Context
csharp
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MyApi;
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = [typeof(JsonStringEnumConverter<Status>)])]
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(List<WeatherForecast>))]
[JsonSerializable(typeof(CreateOrderRequest))]
[JsonSerializable(typeof(OrderResponse))]
[JsonSerializable(typeof(List<OrderResponse>))]
[JsonSerializable(typeof(ErrorResponse))]
public partial class MyApiJsonContext : JsonSerializerContext
{
}
ASP.NET Core Integration
Program.cs:
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolver = MyApiJsonContext.Default;
});
var app = builder.Build();
app.MapGet("/weather", () => new WeatherForecast("Seattle", 72));
app.MapPost("/orders", (CreateOrderRequest request) => Results.Ok(new OrderResponse()));
app.Run();
AOT Configuration
For full AOT compatibility, add to the Directory.Build.props file (preferred), or project file:
xml
<PropertyGroup> <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault> </PropertyGroup>
This ensures compile-time errors if any type is missing from the context.
Notes
- •Context naming: Use
{ProjectName}JsonContextfor consistency - •File location: Place in project root alongside Program.cs
- •Collection types: Always include
List<T>and array types if used in APIs - •Polymorphic types: Run
dotnet-json-polymorphicfirst; polymorphic types use metadata-based generation only - •Enum handling:
JsonStringEnumConverter<TEnum>requires .NET 8+ for AOT support - •Multiple contexts: Can have multiple contexts; use
JsonTypeInfoResolver.Combine()to merge them