Swashbuckle.WebApi
Swashbuckle.WebApi copied to clipboard
Swashbuckle.AspNetCore.Filters throws an exception when trying to use System.Text.Json.Serialization.JsonStringEnumConverter
VERSION:
Swashbuckle.AspNetCore Version="5.0.0-rc5" Swashbuckle.AspNetCore.Filters Version="5.0.0-rc9" Swashbuckle.AspNetCore.Swagger Version="5.0.0-rc5" Swashbuckle.AspNetCore.SwaggerGen Version="5.0.0-rc5" Swashbuckle.AspNetCore.SwaggerUi Version="5.0.0-rc5"
STEPS TO REPRODUCE:
I'm sorry I wanted to create a minimal sample repository so you guys can just clone and check but at my company for security reasons I can't push anything on Github.
Create an ASP.NET 3.1 project.
Here is the Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddVersionedSwagger();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
app.UseRouting();
app.UseVersionedSwagger(provider);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
A VersionedSwaggerExtensions.cs
file declaring some extension methods
public static class VersionedSwaggerExtensions
{
public static IServiceCollection AddVersionedSwagger(this IServiceCollection services)
{
services.AddApiVersioning(o =>
{
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'V'VVV");
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
services.AddSwaggerGen(options =>
{
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
foreach (var apiVersion in provider.ApiVersionDescriptions)
{
ConfigureVersionedDescription(options, apiVersion);
}
});
services.AddSwaggerExamplesFromAssemblyOf<FooRequestExampleProvider>();
return services;
}
private static void ConfigureVersionedDescription(SwaggerGenOptions options, ApiVersionDescription apiVersion)
{
var descriptions = new Dictionary<string, string>
{
{ "1.0", "Version 1.0 of my test API" }
};
var apiVersionName = apiVersion.ApiVersion.ToString();
options.SwaggerDoc(apiVersion.GroupName,
new OpenApiInfo()
{
Title = "Just a test API",
Contact = new OpenApiContact()
{
Name = "Jérôme MEVEL"
},
Version = apiVersionName,
Description = descriptions[apiVersionName]
});
}
public static IApplicationBuilder UseVersionedSwagger(this IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
});
return app;
}
And finally a FooController
[ApiController]
[Route("[controller]")]
public class FooController : ControllerBase
{
/// <summary>
/// Just a test Method
/// </summary>
/// <param name="fooParams">My Params</param>
/// <returns></returns>
[HttpGet("Foo")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(FooResult))]
[SwaggerRequestExample(requestType: typeof(FooQuery), examplesProviderType: typeof(FooRequestExampleProvider), jsonConverter: typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
[SwaggerResponseExample(statusCode: 200, examplesProviderType: typeof(FooResponseExampleProvider), jsonConverter: typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public FooResult Foo([FromQuery]FooQuery fooParams)
{
return new FooResult()
{
Result = "Success"
};
}
}
EXPECTED RESULT:
Swagger generates the API documentation along with query examples
ACTUAL RESULT:
options.SwaggerEndpoint
throws the following exception (didn't include the full stacktrace)
System.InvalidCastException HResult=0x80004002 Message=Unable to cast object of type 'System.Text.Json.Serialization.JsonStringEnumConverter' to type 'Newtonsoft.Json.JsonConverter'. Source=Swashbuckle.AspNetCore.Filters StackTrace: at Swashbuckle.AspNetCore.Filters.SwaggerRequestExampleAttribute..ctor(Type requestType, Type examplesProviderType, Type contractResolver, Type jsonConverter)
ADDITIONAL DETAILS
Here is the StackOverflow question I created
see 'NewtonSoft.JSON Support' here: https://dotnetcoretutorials.com/2020/01/31/using-swagger-in-net-core-3/
Install-Package Swashbuckle.AspNetCore.Newtonsoft
...
services.AddSwaggerGenNewtonsoftSupport();
see 'NewtonSoft.JSON Support' here: https://dotnetcoretutorials.com/2020/01/31/using-swagger-in-net-core-3/
Install-Package Swashbuckle.AspNetCore.Newtonsoft
...services.AddSwaggerGenNewtonsoftSupport();
I think you misunderstood my issue, I don't want to use NewtonSoft, I'm trying to use Swashbuckle along with the new System.Text.Json
package.
The problem is that it seems Swashbuckle is tightly using Newtonsoft.Json.JsonConverter
and there's no way to use System.Text.Json.Serialization.JsonStringEnumConverter
instead.
Internally Swashbuckle is trying to convert the JsonStringEnumConverter
type as a Newtonsoft.Json.JsonConverter
and obviously fails
Yes I am seeing the same thing, i.e. that the attribute for System.Text.Json (not Newtonsoft) with [JsonConverter(typeof(JsonStringEnumConverter))] is not taken into account
Update: added this to Startup.cs and it is working as expected, fyi @jmevel :
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); // this was the key
});
Yes I am seeing the same thing, i.e. that the attribute for System.Text.Json (not Newtonsoft) with [JsonConverter(typeof(JsonStringEnumConverter))] is not taken into account
Update: added this to Startup.cs and it is working as expected, fyi @jmevel :
services.AddControllersWithViews() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); // this was the key });
Thanks for your answer!
I think at the time I wrote this question I didn't know yet how to set a default JSON converter.
However I think it's weird we have to do that. Isn't System.Text.Json
already the default JSON library since .NET Core 3.0?
Swashbuckle is itself shifting to this new library so it should be the default one in any Swashbuckle Nuget package
Hi, I'm the author of https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters
- Is this still an issue with the latest version of the above NuGet?
- If so - then it should be logged at the above GitHub project and not here. This issue here can be closed.
I'm closing this issue as @mattfrear suggested because I created this ticket on the wrong repository. Sorry but I have no idea if this is still an issue as I'm not working with Swashbuckle anymore (not in the company anymore).