-
Notifications
You must be signed in to change notification settings - Fork 5k
JsonSerializerOptions.AddContext should allow context composition #80527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsDescriptionIn ASP.NET Core, builder.Services.ConfigureHttpJsonOptions(
options => options.SerializerOptions.AddContext<CustomAppContext>()); or builder.Services.ConfigureHttpJsonOptions(
options => options.SerializerOptions.TypeInfoResolver = CustomAppContext.Default); Also, users and internally can combine additional Eg.: services.PostConfigure<JsonOptions>(options =>
{
if (options.SerializerOptions.TypeInfoResolver is not null)
{
_serializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(_serializerOptions.TypeInfoResolver, ProblemDetailsJsonContext.Default);
}
}); The biggest challenge is how Related to: dotnet/aspnetcore#45906 Reproduction Stepsusing Microsoft.AspNetCore.Http.Json;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.ConfigureHttpJsonOptions(
options => options.SerializerOptions.AddContext<CustomAppContext>());
// Additional JsonSerializerOptions configuration
builder.Services.PostConfigure<JsonOptions>(options =>
{
options.SerializerOptions.AddContext<AdditionalAppContext>();
});
var app = builder.Build();
app.Run();
public class TypeA { }
[JsonSerializable(typeof(TypeA))]
public partial class CustomAppContext : JsonSerializerContext {}
public class TypeB { }
[JsonSerializable(typeof(TypeB))]
public partial class AdditionalAppContext : JsonSerializerContext { } Expected behaviorThe expected behavior is multiple calls to Actual behaviorThe second call to
Regression?No response Known WorkaroundsIf you have control of the var serializerOptions = options.SerializerOptions;
if (serializerOptions.TypeInfoResolver != null)
{
serializerOptions = new(options.SerializerOptions);
serializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(serializerOptions.TypeInfoResolver!, AdditionalAppContext.Default);
} Configuration.NET 7 Other informationNo response
|
Note: the workaround also has perf hit from fast path not being used currently |
cc #71933 |
Following offline conversation, there is desire to introduce the following (breaking change) to the public void AddContext<TContext>() where TContext : JsonSerializerContext, new()
{
VerifyMutable();
TContext context = new();
- // create bidirectional link between this options instance and the context,
- // making options read-only and overwriting any existing resolver configuration.
- context.Options = this;
- TypeInfoResolver = context;
+ // Do not associate the context with the current options and
+ // chain the current context after any pre-existing resolvers.
+ TypeInfoResolver = TypeInfoResolver is null ? context : JsonTypeInfoResolver.Combine(TypeInfoResolver, context);
} This would introduce the following breaking change: options.TypeInfoResolver = FooContext.Default;
options.AddContext<BarContext>();
Assert.IsType<BarContext>(options.TypeInforesolver); // passes in .NET 7, would start to fail in .NET 8 I feel that this type of breaking change is acceptable, since more likely than not the user is doing something wrong here. We should document the breaking change regardless. |
👍. Also we should be able to just combine the contexts even if one exists already; still a breaking change. Also noting that we discussed keeping track of the originating context to enable using fast-path for combined contexts. |
Fixed in #80698. I'll refrain from creating a breaking change document until we have built confidence that this is the behavior we're shipping in .NET 8. |
Description
In ASP.NET Core,
JsonSerializerOptions
are resolved from DI and is possible to configureJsonTypeResolver/Context
by:or
Also, users and internally can combine additional
JsonTypeInfoResolvers
using Options [post-]configuration.Eg.:
The biggest challenge is how
JsonTypeResolver/Context
composition works, since a call toAddContext
makes theJsonSerializerOptions
read only and locks it to any further modification related to the Resolver.Related to: dotnet/aspnetcore#45906
cc @davidfowl @eiriktsarpalis
Reproduction Steps
Expected behavior
The expected behavior is multiple calls to
AddContext
or a new API (eg.:AppendContext
) combine JsonSerializerContext in the order it is called while keeping the possibility to keep the fast path performance.Actual behavior
The second call to
AddContext
throws anInvalidOperationException
:Regression?
No response
Known Workarounds
If you have control of the
JsonSerializerOptions
(not possible in ASP.NET Core right now) you could create a newJsonSerializerOptions
using the copy constructor andcombine
the additional context:Configuration
.NET 7
Other information
No response
The text was updated successfully, but these errors were encountered: