question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using the System.Text.Json source generator with ASP NET Core throws NotSupportedException when attempting to serialize IEnumerable<T>

See original GitHub issue

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: 11f6b8f712

Runtime 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>

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:27 (22 by maintainers)

github_iconTop GitHub Comments

2reactions
davidfowlcommented, Sep 12, 2022

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).

trim safe, that use async serialization behind the scenes.

ASP.NET Core itself isn’t trim safe so I’m not sure how much this matters for apps today.

1reaction
halter73commented, Sep 15, 2022

Removing labels so the bot doesn’t auto close this again before #43894 is merged.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Custom serialization and deserialization contracts
Json library constructs a JSON contract for each .NET type, which defines how the type should be serialized and deserialized. The contract is ......
Read more >
System.Text.Json source generation not working
I'm trying to add System.Text.Json source generation to one of my projects, a .net6 rest api. When I try to specify the partial...
Read more >
Optimize your API performance with the System.Text.Json ...
Json source generator as a way to improve your API performance. By default the System.Text.Json serializer is using a lot of reflection behind ......
Read more >
Improving logging performance with source generators
There's a new source generator to help with logging using ILogger (the focus of this post); You can use source generators with System.Text.Json...
Read more >
System.Text.Json Source Generators Makes Serialization 40 ...
We looked at how to use Source Generators with System. Text. Json to improve serialization and deserialization performance by up to 40%.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found