aspnetcore: Using the System.Text.Json source generator with ASP NET Core throws NotSupportedException when attempting to serialize IEnumerable
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
This was a scenario that was working in .NET 6.0.
When using the .NET 7.0 System.Text.Json source generator with controllers (this is not reproduceable using minimal APIs), ASP NET Core fails to deserialize IEnumerable<T> results.
NotSupportedException: Metadata for type 'System.Collections.Generic.List`1[JsonSourceGenIEnumerableRepro.MyClass]' was not provided by TypeInfoResolver of type 'JsonSourceGenIEnumerableRepro.JsonContext'. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type type, IJsonTypeInfoResolver resolver)
System.Text.Json.JsonSerializerOptions.GetTypeInfoCached(Type type)
System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type runtimeType)
System.Text.Json.JsonSerializer.SerializeAsync(Stream utf8Json, object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|28_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Calling JsonSerializer.Serialize(enumerable, JsonContext.Default.IEnumerableJsonTypeInfo) manually succeeds.
Expected Behavior
JSON serialization by the ASP NET Core controller infrastructure has the same behaviour as if JsonSerializer.Serialize was called.
Steps To Reproduce
https://github.com/JakeYallop/JsonSourceGeneratorIEnumerableRepro
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace JsonSourceGenIEnumerableRepro;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var l = new List<MyClass> { new(), new() };
//using the STJ source gen default infrastructure succeeds without failing
var output = JsonSerializer.Serialize(l, JsonContext.Default.IEnumerableMyClass);
builder.Services
.AddMvcCore()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.AddContext<JsonContext>();
});
var app = builder.Build();
app.MapControllers();
app.Run();
}
}
[Route("")]
public class MyController : ControllerBase
{
public MyController()
{
}
[HttpGet("")]
public IActionResult Get()
{
var l = new List<MyClass> { new(), new() };
return Ok(l);
}
}
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(IEnumerable<MyClass>))]
public partial class JsonContext : JsonSerializerContext
{
}
public class MyClass
{
public int MyProp { get; set; }
}
Exceptions (if any)
See above
.NET Version
7.0.100-rc.2.22459.2
Anything else?
dotnet --info output (click to view)
``` .NET SDK: Version: 7.0.100-rc.2.22459.2 Commit: 11f6b8f712Runtime Environment: OS Name: Windows OS Version: 10.0.19043 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\7.0.100-rc.2.22459.2\
Host: Version: 7.0.0-rc.2.22451.11 Architecture: x64 Commit: 6d10e4c8bc
.NET SDKs installed: 3.1.422 [C:\Program Files\dotnet\sdk] 5.0.100 [C:\Program Files\dotnet\sdk] 5.0.104 [C:\Program Files\dotnet\sdk] 5.0.214 [C:\Program Files\dotnet\sdk] 5.0.303 [C:\Program Files\dotnet\sdk] 6.0.101 [C:\Program Files\dotnet\sdk] 6.0.108 [C:\Program Files\dotnet\sdk] 6.0.202 [C:\Program Files\dotnet\sdk] 6.0.203 [C:\Program Files\dotnet\sdk] 6.0.400 [C:\Program Files\dotnet\sdk] 7.0.100-rc.2.22459.2 [C:\Program Files\dotnet\sdk]
.NET runtimes installed: Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.0-rc.2.22452.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.0-rc.2.22451.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.0-rc.2.22431.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Other architectures found: arm64 [C:\Program Files\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation] x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
Environment variables: Not set
global.json file: C:\Users\Agmas\source\repos\JsonSourceGenIEnumerableRepro\global.json
Learn more: https://aka.ms/dotnet/info
Download .NET: https://aka.ms/dotnet/download
Environment variables: Not set
global.json file: Not found
Learn more: https://aka.ms/dotnet/info
Download .NET: https://aka.ms/dotnet/download
</details>
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 27 (22 by maintainers)
The change is by design BTW https://github.com/dotnet/runtime/issues/71714. You need to enumerate all types that will be used for serialization when using the source generator and it will no longer fall back to reflection based when it can’t find the type information. In .NET 6 it did fallback and it was working because of that (and you didn’t achieve trim safety as a result).
In .NET 7 now it’ll let you know when your type closure is incomplete and needs to be spelling out more (like in this example).
ASP.NET Core itself isn’t trim safe so I’m not sure how much this matters for apps today.
Removing labels so the bot doesn’t auto close this again before #43894 is merged.
This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.
See our Issue Management Policies for more information.
https://github.com/dotnet/docs/issues/30758 should only concern scenaria where a custom
JsonConverter<object>is registered, so it’s probably unrelated. Could you share a minimal reproduction highlighting the problem?Also, I confirmed that we could fix it using the non-generic overload and passing the runtime type directly. @dotnet/area-system-text-json do you have any other suggestions since that is exactly what you were doing before and changed in the commit I mentioned?
I don’t think that this is the same underlying issue.
In #43236, the source generated context has no direct reference to the underlying ProblemDetails type, whereas is this case the context has all the information it needs (we have explicitly opted in with a
JsonSerializableattribute on the context), as demonstrated by the fact that serialization viaJsonSerializer.Serializeusing the source-generatedJsonTypeInfois successful.There should be no need for a custom type resolver in this case.
Interestingly, using a
List<MyClass>instead of anIEnumerable<MyClass>appears to work without an issue.