Description
Description
Hello, I'm currently upgrading from .NET 6 to .NET 9. Everything goes fine, but JSON serialization. I'm trying to use the default source generation option for better performance but ran into problems during OpenAPI document construction.
An application I'm developing uses controllers to provide a set of APIs to its clients. Since the start of an upgrade process, I've decided to create a set of small and focused serializer contexts rather than one big and clumsy context per application. This decision resulted in around 39 source generator contexts, which I combine during the startup phase by using TypeInfoResolverChain.Add
method. Everything compiles fine, but when I request the OpenApi document via a browser's GET
at /openapi/v1.json
I get a NotSupportedException
error.
All models used inside of the API controllers are public sealed classes, using only .NET types (ints, booleans, Guids, etc.), and do not reference any custom objects as their property members. The type displayed in that error was correctly included in its respective serialization context class:
That context later combines with other 38 contexts via:
During a debug session I check if the TyperInfoRsolverChain contains the context needed for successful serialization of the requested object's schema.
Here is an immediate window's output:
? options.JsonSerializerOptions.TypeInfoResolverChain
Count = 39, IsReadOnly = False
...
[24]: {Tntu.Automated.ServicesLayer.Rest.WebApi.Controllers.Worker.PersonWorkModelsContext}
...
?options.JsonSerializerOptions.TypeInfoResolverChain[24]
{Tntu.Automated.ServicesLayer.Rest.WebApi.Controllers.Worker.PersonWorkModelsContext}
DateTime: Type = DateTime, Kind = None
Decimal: Type = Decimal, Kind = None
GeneratedSerializerOptions: TypeInfoResolver = <null>, IsReadOnly = False
Guid: Type = Guid, Kind = None
Int32: Type = Int32, Kind = None
NullableDateTime: Type = Nullable`1, Kind = None
NullableDecimal: Type = Nullable`1, Kind = None
NullableInt32: Type = Nullable`1, Kind = None
Options: TypeInfoResolver = Tntu.Automated.ServicesLayer.Rest.WebApi.Controllers.Worker.PersonWorkModelsContext, IsReadOnly = True
PersonWorkCreateParams: Type = PersonWorkCreateParams, Kind = Object
PersonWorkEditParams: Type = PersonWorkEditParams, Kind = Object
PersonWorkEntity: Type = PersonWorkEntity, Kind = Object
String: Type = String, Kind = None
_DateTime: Type = DateTime, Kind = None
_Decimal: Type = Decimal, Kind = None
_Guid: Type = Guid, Kind = None
_Int32: Type = Int32, Kind = None
_NullableDateTime: Type = Nullable`1, Kind = None
_NullableDecimal: Type = Nullable`1, Kind = None
_NullableInt32: Type = Nullable`1, Kind = None
_PersonWorkCreateParams: Type = PersonWorkCreateParams, Kind = Object
_PersonWorkEditParams: Type = PersonWorkEditParams, Kind = Object
_PersonWorkEntity: Type = PersonWorkEntity, Kind = Object
_String: Type = String, Kind = None
What can go wrong here? Does the Microsoft.AspNetCore.OpenApi
package use source generators or do I need to revert back to a reflection mechanism?
Expected behavior
Contents of v1.json is up and ready
Actual behavior
Error message:
NotSupportedException: JsonTypeInfo metadata for type 'Tntu.Automated.ServicesLayer.Rest.WebApi.Models.Output.Worker.PersonWorkEntity' was not provided by TypeInfoResolver of type 'System.Text.Json.Serialization.Metadata.JsonTypeInfoResolverWithAddedModifiers'. If using source generation, ensure that all root types passed to the serializer have been annotated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
Update 3/17/2025 3:27 PM
Full stack trace:
System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type type, IJsonTypeInfoResolver resolver)
System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, bool ensureConfigured, Nullable<bool> ensureNotNull, bool resolveIfMutable, bool fallBackToNearestAncestorType)
System.Text.Json.Schema.JsonSchemaExporter.GetJsonSchemaAsNode(JsonSerializerOptions options, Type type, JsonSchemaExporterOptions exporterOptions)
Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.CreateSchema(OpenApiSchemaKey key)
System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func<OpenApiSchemaKey, JsonNode> valueFactory)
Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, bool captureSchemaByRef, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponseAsync(ApiDescription apiDescription, int statusCode, ApiResponseType apiResponseType, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponsesAsync(ApiDescription description, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet<OpenApiTag> capturedTags, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping<string, ApiDescription> descriptions, HashSet<OpenApiTag> capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet<OpenApiTag> capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions+<>c__DisplayClass0_0+<<MapOpenApi>b__0>d.MoveNext()
Microsoft.AspNetCore.Http.Generated.<GeneratedRouteBuilderExtensions_g>F56B68D2B55B5B7B373BA2E4796D897848BC0F04A969B1AF6260183E8B9E0BAF2__GeneratedRouteBuilderExtensionsCore+<>c__DisplayClass2_0+<<MapGet0>g__RequestHandler|5>d.MoveNext()
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)