Skip to content

OpenAPI document generation fails for JsonNumberHandling.WriteAsString and a ValidationAttribute #66111

@CaringDev

Description

@CaringDev

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

A vanilla WebAPI app fails to generate the OpenAPI document if JsonNumberHandling.WriteAsString is set and validation attributes are present.

Yes, I'm aware that the behavior of JsonNumberHandling.WriteAsString is not defined by the spec. However, I'd like safely to roundtrip longs (so I'm also setting JsonNumberHandling.AllowReadingFromString) and my client-side stack can handle the schema and data.

Expected Behavior

OpenAPI document is generated:

// ...
"summary": {
  "maxLength": 100,
  "minLength": 0,
  "type": [
    "null",
    "string"
  ]
}
// ...

Steps To Reproduce

dotnet new webapi -n Repro
cd Repro
dotnet add package Microsoft.Extensions.ApiDescription.Server --version 10.0.5
dotnet build
  • ./obj/Repro.json is generated correctly
  • change the following in Program.cs
builder.Services.ConfigureHttpJsonOptions(o =>
    o.SerializerOptions.NumberHandling = JsonNumberHandling.WriteAsString);

// ...

record WeatherForecast(DateOnly Date, int TemperatureC, [property: StringLength(100)] string? Summary)

// ...

Exceptions (if any)

dotnet build fails with an exception, also the OpenAPI document at /openapi/v1.json cannot be generated for the same reason.

System.AggregateException: One or more errors occurred. (The JSON value could not be converted to OpenApiJsonSchema. Path: $ | LineNumber: 0 | BytePositionInLine: 74.)
 ---> System.Text.Json.JsonException: The JSON value could not be converted to OpenApiJsonSchema. Path: $ | LineNumber: 0 | BytePositionInLine: 74.
 ---> System.InvalidOperationException: Cannot get the value of a token type 'String' as a number.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(JsonTokenType tokenType)
   at System.Text.Json.Utf8JsonReader.TryGetInt32(Int32& value)
   at System.Text.Json.Utf8JsonReader.GetInt32()
   at OpenApiJsonSchema.ReadProperty(Utf8JsonReader& reader, String propertyName, OpenApiSchema schema, JsonSerializerOptions options)
   at OpenApiJsonSchema.JsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
   --- End of inner exception stack trace ---
   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObject(Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadAsObject(Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo)
   at OpenApiJsonSchema.ReadDictionary[T](Utf8JsonReader& reader)
   at OpenApiJsonSchema.ReadProperty(Utf8JsonReader& reader, String propertyName, OpenApiSchema schema, JsonSerializerOptions options)
   at OpenApiJsonSchema.JsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at OpenApiJsonSchema.ReadProperty(Utf8JsonReader& reader, String propertyName, OpenApiSchema schema, JsonSerializerOptions options)
   at OpenApiJsonSchema.JsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromNode[TValue](JsonNode node, JsonTypeInfo`1 jsonTypeInfo)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateUnresolvedSchemaAsync(OpenApiDocument document, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(OpenApiDocument document, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponseAsync(OpenApiDocument document, ApiDescription apiDescription, Int32 statusCode, ApiResponseType apiResponseType, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponsesAsync(OpenApiDocument document, ApiDescription description, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, OpenApiDocument document, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, OpenApiDocument document, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(OpenApiDocument document, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, HttpRequest httpRequest, CancellationToken cancellationToken)
   at Microsoft.Extensions.ApiDescriptions.OpenApiDocumentProvider.GenerateAsync(String documentName, TextWriter writer, OpenApiSpecVersion openApiSpecVersion)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait(TimeSpan timeout, CancellationToken cancellationToken)
   at Microsoft.Extensions.ApiDescription.Tool.Commands.GetDocumentCommandWorker.GetDocument(String documentName, String projectName, String outputDirectory, MethodInfo generateMethod, Object service, MethodInfo generateWithVersionMethod, String fileName)
   at Microsoft.Extensions.ApiDescription.Tool.Commands.GetDocumentCommandWorker.GetDocuments(IServiceProvider services)
   at Microsoft.Extensions.ApiDescription.Tool.Commands.GetDocumentCommandWorker.Process()

where each line starts with path\to\microsoft.extensions.apidescription.server\10.0.5\build\Microsoft.Extensions.ApiDescription.Server.targets(67,5): error

.NET Version

10.0.201

Anything else?

CLI, all packages 10.0.5

Metadata

Metadata

Assignees

Labels

area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions