Swashbuckle.AspNetCore: Enum string converter is not respected using .NET 6 minimal API

Using Swashbuckle.AspNetCore 6.2.3 with ASP.NET 6 minimal API, I have configured JSON serializer options per the docs.

services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
    options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});

However, Swagger UI still reports enums as ints unless I add also use AddControllers().AddJsonOptions():

services.AddControllers()
	.AddJsonOptions(options =>
	{
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
            options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;	
        });

Once the second block is added, the swagger UI output references the enum string values as expected. As far as I know, the latter simply does the former under the hood, but there seems to be some order of operations affecting the schema generation here?

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 22
  • Comments: 18

Most upvoted comments

I ran into the same problem and my colleague suggested the following as a workaround, which works for me:

using Microsoft.AspNetCore.Http.Json;
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;
.....
builder.Services.Configure<JsonOptions>(o => o.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.Configure<MvcJsonOptions>(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

You can always use NewtonsoftJson it is in nuget <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.2" />

here are my settings .AddNewtonsoftJson(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.Converters.Add(new StringEnumConverter()); options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; });

Swashbuckle.AspNetCore 6.4.0 picks up changes from “Microsoft.AspNetCore.Mvc.JsonOptions” options. However, minimal APIs are using “Microsoft.AspNetCore.Http.Json.JsonOptions”. Therefore, both of them needs to be configured for Swagger schemas to match the API output.

It would be nice if Swashbuckle can be configured to use “Microsoft.AspNetCore.Http.Json.JsonOptions”.

The below workaround works on the swagger schema.

builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
	options.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
builder.Services.Configure<Microsoft.AspNetCore.Mvc.JsonOptions>(options =>
{
	options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});

However, I had to use AddContollers to get it working with the swagger example.

builder.Services.AddControllers()
    .AddJsonOptions(options =>
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

Has anyone been able to get it working with the swagger example without adding AddControllers?

@wrkntwrkn and @stvwndr helped lead me to the workaround that I’m using:

builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
	options.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
builder.Services.Configure<Microsoft.AspNetCore.Mvc.JsonOptions>(options =>
{
	options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});

@wrkntwrkn and @stvwndr helped lead me to the workaround that I’m using:

builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
	options.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
builder.Services.Configure<Microsoft.AspNetCore.Mvc.JsonOptions>(options =>
{
	options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});

Thank you, this works as expected!

To anyone facing a similar issue as me, make sure you have the right namespace when registerting your serializer globally.

NOT - Microsoft.AspNetCore.Mvc - JsonOptions YES - Microsoft.AspNetCore.Http.Json - JsonOptions

It was in the docs but i missed it when migrating to minimal api’s since the namings are ver similar.

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum DemoEnum

Thanks this works for me. But of course decorating all enums with that is far from ideal, it would be nice if ASPNET respected the serializer converter.

I managed to get this fully working for minimal API scenarios by decorating the enum type itself with JsonConverterAttribute.

Tested on NET7 and NET8 RC1:

using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;

// var builder = WebApplication.CreateSlimBuilder(args); -NET8-
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// OK
app.MapPost("postDemo", (RequestDto dto) => TypedResults.Ok(new ResponseDto(dto.Enum)));

// Does not work, query param schema is string
app.MapGet("getDemo1", (DemoEnum? enumParameter) => TypedResults.Ok(new ResponseDto(enumParameter)));

// OK
app.MapGet("getDemo2", ([FromQuery] DemoEnum? enumParameter) => TypedResults.Ok(new ResponseDto(enumParameter)));

// OK
app.MapGet("getDemo3", ([AsParameters] Parameters parameters) => TypedResults.Ok(new ResponseDto(parameters.EnumParameter)));

app.UseSwagger();
app.UseSwaggerUI();
app.Run();

public struct Parameters
{
    [FromQuery]
    public DemoEnum? EnumParameter { get; set; }
}

public sealed record RequestDto(DemoEnum? Enum);
public sealed record ResponseDto(DemoEnum? Enum);

// [JsonConverter(typeof(JsonStringEnumConverter<DemoEnum>))] -NET8-
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum DemoEnum
{
    FirstValue,
    SecondValue,
}

Looking into this some more, it looks like the problem may be in SwaggerGenServiceCollectionExtensions.cs, line 34:

#if (!NETSTANDARD2_0)
                var serializerOptions = s.GetService<IOptions<JsonOptions>>()?.Value?.JsonSerializerOptions
                    ?? new JsonSerializerOptions();
#else
                var serializerOptions = new JsonSerializerOptions();
#endif

The problem here is that the JsonOptions type is pulled from the Microsoft.AspNetCore.Mvc namespace while .NET 6 minimal APIs are documented to use JsonOptions from the Microsoft.AspNetCore.Http.Json namespace which this code seems blissfully unaware of. Not sure what the path forward is.