domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.25k stars 1.31k forks source link

JsonProperty(ItemConverterType = typeof(StringEnumConverter)) not recognized without DescribeAllEnumsAsStrings() #1349

Closed artfulsage closed 4 years ago

artfulsage commented 4 years ago

Hi. In Swashbuckle.AspNetCore 5.0.0-rc4 method SwaggerGenOptions.DescribeAllEnumsAsStrings() marked as Obsolete. But without it attribute [Newtonsoft.Json.JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] is not working properly. See example below.

public class Program
{
    public static void Main(string[] args)
        => Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(b => b.UseStartup<Startup>())
            .Build()
            .Run();
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
        => services
            .AddSwaggerGen(c => c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" }))
            .AddControllers();

    public void Configure(IApplicationBuilder app)
        => app
            .UseSwagger()
            .UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"))
            .UseRouting()
            .UseEndpoints(endpoints => endpoints.MapControllers());
}

[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter), true)]
public enum OkEnum
{
    Freezing, Bracing, Chilly, Cool
}

public enum BadEnum
{
    Freezing, Bracing, Chilly, Cool
}

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public OkEnum OkValue { get; set; }
    [Newtonsoft.Json.JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
    public List<BadEnum> BadValues { get; set; }
}

[ApiController]
[Route("weather")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
        => Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            OkValue = OkEnum.Cool,
            BadValues = new List<BadEnum> { BadEnum.Cool }
        });
}

sw

With SwaggerGenOptions.DescribeAllEnumsAsStrings()

sw3

Mds92 commented 4 years ago

I have the same problem

domaindrivendev commented 4 years ago

@artfulsage can you elaborate a little more on your usecase? I do understand the issue you're reporting but I guess I'm not understanding why you wouldn't just use the JsonConverterAttribute on the enum definition itself (as opposed to the property that uses it) - i.e. as you've demonstrated with OkEnum above.

The only reason I can think of to NOT do this is if you wanted to serialize the enum as a string in some cases (e.g. when part of a certain property) and as an integer in others. However, the DescribeAllEnumsAsStrings is a global setting applied to all enums and therefore wouldn't support this scenario anyway.

ddeisadzevp commented 4 years ago

I have the same problem (using 5.0.0 RC-5). I have tried adding a global JsonConvert (StringEnumConverter) to json serializer settings along with DescribeAllEnumsAsStrings, just DescribeAllEnumsAsStrings , StringEnumConverter on the enum itself. None of these options seem to work, the enums still return as integers. I am not sure what other information to display for this issue, but let me know.

jcoutch commented 4 years ago

@artfulsage - You're using Newtonsoft attributes, but Swashbuckle.AspNetCore 5.x uses System.Text.Json by default.

To fix this, either:

artfulsage commented 4 years ago

@domaindrivendev, @jcoutch thanks for your feedback!

ddeisadzevp commented 4 years ago

@domaindrivendev it might be helpful to add that to the docs/WIKI (FAQ) : services.AddSwaggerGenNewtonsoftSupport() for future reference, seems like it is a commonly asked question

jcoutch commented 4 years ago

@ddeisadzevp - I found it right on the main page: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#systemtextjson-stj-vs-newtonsoft

tobyartisan commented 4 years ago

@artfulsage, when you use services.AddSwaggerGenNewtonsoftSupport(), then ensure that you add it after you add services.AddSwaggerGen(...); otherwise, it won't work.

artfulsage commented 4 years ago

@domaindrivendev @jcoutch @tobyartisan perhaps I do something wrong but this code still does not work

netcoreapp3.1 Newtonsoft.Json 12.0.3 Swashbuckle.AspNetCore 5.2.1 Swashbuckle.AspNetCore.Newtonsoft 5.2.1

public class Program
{
  public static void Main(string[] args)
    => Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(b => b.UseStartup<Startup>())
      .Build()
      .Run();
}

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services
      .AddMvc()
      .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
      .AddNewtonsoftJson();

    services
      .AddSwaggerGen(c => c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" }))
      .AddSwaggerGenNewtonsoftSupport();
  }

  public void Configure(IApplicationBuilder app)
    => app
      .UseSwagger()
      .UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"))
      .UseRouting()
      .UseEndpoints(endpoints => endpoints.MapControllers());
}

public enum BadEnum
{
  Freezing, Bracing, Chilly, Cool
}

public class WeatherForecast
{
  public DateTime Date { get; set; }
  [Newtonsoft.Json.JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
  public List<BadEnum> BadValues { get; set; }
}

[ApiController]
[Route("weather")]
public class WeatherForecastController : ControllerBase
{
  [HttpGet]
  public IEnumerable<WeatherForecast> Get()
    => Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
      Date = DateTime.Now.AddDays(index),
      BadValues = new List<BadEnum> { BadEnum.Cool }
    });
}

Screenshot_2020-03-24 Swagger UI

Screenshot_2020-03-24 Screenshot

simonvane commented 4 years ago

@artfulsage Do you need to do this bit?

.AddJsonOptions(options => {
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
});
domaindrivendev commented 4 years ago

@artfulsage - unfortunately ItemConverterType isn't currently supported. As mentioned above, if you decorate your enum class directly as follows - the enum will be described correctly.

[JsonConverter(typeof(StringEnumConverter))]
public enum BadEnum
{
    Freezing, Bracing, Chilly, Cool
}

However, it seems this may not be feasible for you. As many of the comments in this issue have deviated from the original, I think it would be best to create a new issue that specifically addresses the lack of support for enum serialization behavior that's configured via ItemConverterType