242

Is there a way to display all enums as their string value in swagger instead of their int value?

I want to be able to submit POST actions and put enums according to their string value without having to look at the enum every time.

I tried DescribeAllEnumsAsStrings but the server then receives strings instead of the enum value which is not what we're looking for.

Has anyone solved this?

Edit:

public class Letter 
{
    [Required]
    public string Content {get; set;}

    [Required]
    [EnumDataType(typeof(Priority))]
    public Priority Priority {get; set;}
}


public class LettersController : ApiController
{
    [HttpPost]
    public IHttpActionResult SendLetter(Letter letter)
    {
        // Validation not passing when using DescribeEnumsAsStrings
        if (!ModelState.IsValid)
            return BadRequest("Not valid")

        ..
    }

    // In the documentation for this request I want to see the string values of the enum before submitting: Low, Medium, High. Instead of 0, 1, 2
    [HttpGet]
    public IHttpActionResult GetByPriority (Priority priority)
    {

    }
}


public enum Priority
{
    Low, 
    Medium,
    High
}
  • 2
    Do you want the schema to describe the value as a string but then post an integer to the server? JSON.net will handle both values fine, so is the integer only version a definite requirement? I don't think Swagger supports a enum type with both the string and integer value. – Hux Apr 07 '16 at 10:43
  • 1
    Your expected behavior is unclear, can you better explain what you want Swagger UI to display and what you want to POST/PUT to your Web API with examples? – Federico Dipuma Apr 07 '16 at 13:13
  • Moreover, if I have GET methods that take enum in the url, I want the scheme to describe it as strings in the drop down list of suggested values –  Apr 07 '16 at 18:24
  • Why does integer validation fail? The type should be an enum in the model and the json media formatter would correctly handle either a string or int. If you update the question with an example it would help us understand why the validation is failing. – Hux Apr 08 '16 at 11:08
  • 7
    If it's a flags enum, the it has to be numeric, unless you have enum values defined for every possible combination of flags. It's nuts that swagger doesn't display BOTH the name and value for each enum, and instead displays number alone (useless) or names alone (again, useless for flags which must be specified as numbers). – Triynko Feb 23 '18 at 19:48

31 Answers31

278

For ASP.NET Core 3 with the Microsoft JSON library (System.Text.Json)

In Startup.cs/ConfigureServices():

services
    .AddControllersWithViews(...) // or AddControllers() in a Web API
    .AddJsonOptions(options => 
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

For ASP.NET Core 3 with the Json.NET (Newtonsoft.Json) library

Install the Swashbuckle.AspNetCore.Newtonsoft package.

In Startup.cs/ConfigureServices():

services
    .AddControllersWithViews(...)
    .AddNewtonsoftJson(options => 
        options.SerializerSettings.Converters.Add(new StringEnumConverter()));
// order is vital, this *must* be called *after* AddNewtonsoftJson()
services.AddSwaggerGenNewtonsoftSupport();

For ASP.NET Core 2

In Startup.cs/ConfigureServices():

services
    .AddMvc(...)
    .AddJsonOptions(options => 
        options.SerializerSettings.Converters.Add(new StringEnumConverter()));

Pre-ASP.NET Core

httpConfiguration
    .EnableSwagger(c => 
        {
            c.DescribeAllEnumsAsStrings();
        });
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Lee Richardson
  • 8,331
  • 6
  • 42
  • 65
  • 26
    Problem of using options.SerializerSettings.Converters.Add(new StringEnumConverter())) is that your changing the json for all your methods, not only for Sawshbuckle. – Guillaume Dec 17 '19 at 17:25
  • Does anyone have a solution for Azure Functions v2 and/or v3? – Dan Friedman Apr 29 '20 at 17:07
  • @DanFriedman Considering Swashbuckle doesn't work with Azure Functions at all, you're out of luck. – Ian Kemp Aug 12 '20 at 15:10
  • @IanKemp There is third party support with the `AzureExtensions.Swashbuckle` package but like @DanFriedman I cannot get the enum-to-string working as expected – wolfyuk Aug 13 '20 at 07:47
  • If you're configuring for ASP.NET Core 3 with Newtonsoft, the `AddSwaggerGenNewtonsoftSupport()` extension method is available from NuGet package`Swashbuckle.AspNetCore.Newtonsoft`. Read more here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#systemtextjson-stj-vs-newtonsoft – Andy Vaal Sep 16 '20 at 16:22
  • `AddNewtonsoftJson` accepts `IMvcBuilder`. It won't work with `IServiceCollection`. Please fix your code for For ASP.NET Core 3 with the Json.NET (Newtonsoft.Json) library. – Azimuth May 03 '21 at 06:46
  • 3
    and for NET 6 minimal API ? – Kiquenet Jun 30 '22 at 09:53
  • minimal api is same if you add controllers like the comment about web api. `builder.Services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));` – Tyeth May 17 '23 at 18:01
276

Enable globally

From the docs:

httpConfiguration
    .EnableSwagger(c => 
        {
            c.SingleApiVersion("v1", "A title for your API");
            
            c.DescribeAllEnumsAsStrings(); // this will do the trick
        });

Enum/string conversion on particular property

Also, if you want this behavior only on a particular type and property, use the StringEnumConverter:

public class Letter 
{
    [Required]
    public string Content {get; set;}

    [Required]
    [EnumDataType(typeof(Priority))]
    [JsonConverter(typeof(StringEnumConverter))]
    public Priority Priority {get; set;}
}

If you're using Newtonsoft and Swashbuckle v5.0.0 or higher

You'll also need this package:

Swashbuckle.AspNetCore.Newtonsoft

And this in your startup:

services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen()

There's docs here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft

harvzor
  • 2,832
  • 1
  • 22
  • 40
Xavero
  • 3,333
  • 1
  • 15
  • 17
  • 12
    this doesn't work for me.[EnumDataType(typeof(Priority))] [JsonConverter(typeof(StringEnumConverter))] – Lineker Aug 31 '17 at 14:49
  • @NH. yes, i used newtonsoft.json – Lineker Nov 01 '17 at 21:26
  • @Lineker, post your error as a new question, following this guide: https://stackoverflow.com/help/mcve – NH. Nov 02 '17 at 00:05
  • Thx! I think I might just leave your comment in the source too #thiswilldothetrick – Simon_Weaver Jan 04 '18 at 08:14
  • Hmm why is EnumDataType required? Is there a way to make it smart enough to auto detect – Simon_Weaver Jan 04 '18 at 08:19
  • if you use the line with "#thiswilldothetrick", you dont need the StringEnumConverterAttribute. The Attibutes are only required when you want this feature in a specific Property. Also, I dont remember what the EnumDataTypeAttribute does, but perphaps you dont need it. – Xavero Jan 04 '18 at 17:25
  • ASP.NET Core documentation: [Customize Schema for Enum Types](https://github.com/domaindrivendev/Swashbuckle.AspNetCore#customize-schema-for-enum-types) – spottedmahn Aug 27 '18 at 19:18
  • worked instantly for me. How do I make them lower case now? – Jerico Sandhorn Aug 29 '18 at 14:53
  • I think the Lowercase its achieved with the Json converter/Newtonsoft, but i am not sure. – Xavero Aug 30 '18 at 12:27
  • Oh, nevermind, sppotedmahn's link shows how to do it: c.DescribeStringEnumsInCamelCase(); – Xavero Aug 30 '18 at 12:29
  • 9
    `DescribeAllEnumsAsStrings` worked for object properties and even query parameters on controller actions. However, using `EnumDataTypeAttribute` and `JsonConverter(typeof(StringEnumConverter))` did not work for me. – bugged87 Oct 23 '18 at 22:19
  • 7
    This solution omits **important** step of registering StringEnumConverter as a converter in AddNewtonsoftJson section of AddMvc. See example in @Roman Starkov answer below. – A. Tretiakov Feb 11 '20 at 12:52
  • in core2.0 it displays the keys, but not the values as expected. also data member description not works see https://stackoverflow.com/questions/64764537/net-core-2-0-swagger-enum-display – developer learn999 Nov 10 '20 at 17:58
  • 25
    `c.DescribeAllEnumsAsStrings();` is deprecated in Swagger 5.6.0. – computercarguy Dec 10 '20 at 18:09
  • 1
    Where can I get the `httpConfiguration` object? – manymanymore May 19 '21 at 07:51
  • 2
    @computercarguy I had to use `c.UseInlineDefinitionsForEnums();` instead. – Caio Campos Oct 26 '22 at 00:19
54

So I think I have a similar problem. I'm looking for swagger to generate enums along with the int -> string mapping. The API must accept the int. The swagger-ui matters less, what I really want is code generation with a "real" enum on the other side (android apps using retrofit in this case).

So from my research this ultimately seems to be a limit of the OpenAPI specification which Swagger uses. It's not possible to specify names and numbers for enums.

The best issue I've found to follow is https://github.com/OAI/OpenAPI-Specification/issues/681 which looks like a "maybe soon" but then Swagger would have to be updated, and in my case Swashbuckle as well.

For now my workaround has been to implement a document filter that looks for enums and populates the relevant description with the contents of the enum.

        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                {
                    c.DocumentFilter<SwaggerAddEnumDescriptions>();

                    //disable this
                    //c.DescribeAllEnumsAsStrings()

SwaggerAddEnumDescriptions.cs:

using System;
using System.Web.Http.Description;
using Swashbuckle.Swagger;
using System.Collections.Generic;

public class SwaggerAddEnumDescriptions : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        // add enum descriptions to result models
        foreach (KeyValuePair<string, Schema> schemaDictionaryItem in swaggerDoc.definitions)
        {
            Schema schema = schemaDictionaryItem.Value;
            foreach (KeyValuePair<string, Schema> propertyDictionaryItem in schema.properties)
            {
                Schema property = propertyDictionaryItem.Value;
                IList<object> propertyEnums = property.@enum;
                if (propertyEnums != null && propertyEnums.Count > 0)
                {
                    property.description += DescribeEnum(propertyEnums);
                }
            }
        }

        // add enum descriptions to input parameters
        if (swaggerDoc.paths.Count > 0)
        {
            foreach (PathItem pathItem in swaggerDoc.paths.Values)
            {
                DescribeEnumParameters(pathItem.parameters);

                // head, patch, options, delete left out
                List<Operation> possibleParameterisedOperations = new List<Operation> { pathItem.get, pathItem.post, pathItem.put };
                possibleParameterisedOperations.FindAll(x => x != null).ForEach(x => DescribeEnumParameters(x.parameters));
            }
        }
    }

    private void DescribeEnumParameters(IList<Parameter> parameters)
    {
        if (parameters != null)
        {
            foreach (Parameter param in parameters)
            {
                IList<object> paramEnums = param.@enum;
                if (paramEnums != null && paramEnums.Count > 0)
                {
                    param.description += DescribeEnum(paramEnums);
                }
            }
        }
    }

    private string DescribeEnum(IList<object> enums)
    {
        List<string> enumDescriptions = new List<string>();
        foreach (object enumOption in enums)
        {
            enumDescriptions.Add(string.Format("{0} = {1}", (int)enumOption, Enum.GetName(enumOption.GetType(), enumOption)));
        }
        return string.Join(", ", enumDescriptions.ToArray());
    }

}

This results in something like the following on your swagger-ui so at least you can "see what you're doing": enter image description here

zdarsky.peter
  • 6,188
  • 9
  • 39
  • 60
Rory
  • 3,270
  • 1
  • 20
  • 23
  • 1
    +1 I was looking to add descriptions to enums (just to 'describe enum'), never thought of this. I already have misc filters in place, but was looking for something more 'organic', but there's no support. Well then, filters all the way :) – NSGaga-mostly-inactive Nov 16 '17 at 10:29
  • Thanks! I used this in my project, but modified it to work with .NET Core. I added my implementation as an answer. – Gabriel Luci Apr 20 '18 at 12:37
49

ASP.NET Core 3.1

To generate enums as strings using Newtonsoft JSON you must explicitly add Newtonsoft support by adding AddSwaggerGenNewtonsoftSupport() as follows:

services.AddMvc()
    ...
    .AddNewtonsoftJson(opts =>
    {
        opts.SerializerSettings.Converters.Add(new StringEnumConverter());
    });


services.AddSwaggerGen(...);
services.AddSwaggerGenNewtonsoftSupport(); //

This is available via a new package, Swashbuckle.AspNetCore.Newtonsoft. It looks like everything else works fine without this package apart from enum converter support.

Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • 2
    It helps to setup this convention globally, but if you need to apply this only to certain types of enums, you will need to carefully read this [issue](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1459). TL; DR: It's not possible to apply new StringEnumConverter() to property only, but you can apply it to the whole enum type. – A. Tretiakov Feb 11 '20 at 22:39
  • 2
    I suppose if we're talking of gotchas, it's also not possible to use a completely custom converter. Swagger doesn't run the enum values through the custom converter; it simply recognizes `StringEnumConverter` as a special case. – Roman Starkov Feb 12 '20 at 17:14
  • 4
    What is the recommended solution in .net 5? – Maulik Modi May 30 '21 at 06:11
39

.NET CORE 3.1 and SWAGGER 5

if you need a simple solution to selectively make enums passed as strings:

using System.Text.Json.Serialization;


[JsonConverter(typeof(JsonStringEnumConverter))]
public enum MyEnum
{
    A, B
}

Note, we use System.Text.Json.Serialization namespace, not the Newtonsoft.Json!

VeganHunter
  • 5,584
  • 2
  • 26
  • 26
  • 1
    This one works showing the proper values, and also works when converting the values back to the enum. Note that you need to add the NuGet package `System.Text.Json`. – MovGP0 Jun 04 '20 at 11:40
  • 1
    That's what I was looking for! As I have to use string for just single enum, and `DescribeAllEnumsAsStrings` will convert all enums to the string. – Nilay Mehta Jun 13 '20 at 05:13
  • 2
    Thanks for this simple solution. I'm using .NET Core 3.1 and Swagger 5.5. didn't need to use DescribeAllEnumsAsStrings . Just set [JsonConverter(typeof(JsonStringEnumConverter))] on enum. EX: System.Text.Json.Serialization; [JsonConverter(typeof(JsonStringEnumConverter))] public enum Category { Autos, Electronics, Furniture, House, Pets, Miscellaneous } – Mahesh Sep 11 '20 at 17:33
26

My variant for enum stings with values:

enter image description here

Configure Services:

services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "web server api", Version = "v1" });
                c.SchemaFilter<EnumSchemaFilter>();
            });

Filter:

public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                model.Enum.Clear();
                Enum.GetNames(context.Type)
                    .ToList()
                    .ForEach(name => model.Enum.Add(new OpenApiString($"{Convert.ToInt64(Enum.Parse(context.Type, name))} - {name}")));
            }
        }
    }
Andrew Zaitsev
  • 323
  • 3
  • 4
  • this works only on Core3.0, filter not exist in Core2 – developer learn999 Nov 10 '20 at 17:55
  • It works in .Net 5. DescribeAllEnumsAsStrings (deprecated) and AddSwaggerGenNewtonsoftSupport is not working in .Net 5. – sasi reka Feb 01 '21 at 10:11
  • 1
    Only thing that worked for me for what I wanted. – HenryMigo Jun 25 '21 at 14:42
  • I spent three days looking for this. Tried stuff that involved other libraries, but didn't give me what I needed. Here you go showing me how to do it in 4 lines of code. Thanks! – Rich Hopkins Feb 01 '22 at 15:27
  • 10
    This will render your swagger requests unusable as it will overwrite your int representation of value. Use with caution. – OhWelp Feb 17 '22 at 17:59
  • 1
    @OhWelp You are absolutely right! Do you know how this could be split? To return this string in the schema for convenience (to know the string behind each number), but keep the mock data for the requests with just a number as it is by default? – Nikolai Jul 11 '22 at 18:08
  • 1
    I found a solution: `model.Example = new OpenApiInteger(Convert.ToInt32(Enum.Parse(context.Type, names.First())));` - names is the `Enum.GetNames(context.Type).ToList()` – Nikolai Jul 11 '22 at 18:35
25

I wanted to use rory_za's answer in a .NET Core application, but I had to modify it a bit to make it work. Here is the implementation I came up with for .NET Core.

I also changed it so it doesn't assume the underlying type is int, and use new lines between the values for easier reading.

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class EnumDocumentFilter : IDocumentFilter {
    /// <inheritdoc />
    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) {
        // add enum descriptions to result models
        foreach (var schemaDictionaryItem in swaggerDoc.Definitions) {
            var schema = schemaDictionaryItem.Value;
            foreach (var propertyDictionaryItem in schema.Properties) {
                var property = propertyDictionaryItem.Value;
                var propertyEnums = property.Enum;
                if (propertyEnums != null && propertyEnums.Count > 0) {
                    property.Description += DescribeEnum(propertyEnums);
                }
            }
        }

        if (swaggerDoc.Paths.Count <= 0) return;

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values) {
            DescribeEnumParameters(pathItem.Parameters);

            // head, patch, options, delete left out
            var possibleParameterisedOperations = new List<Operation> {pathItem.Get, pathItem.Post, pathItem.Put};
            possibleParameterisedOperations.FindAll(x => x != null)
                .ForEach(x => DescribeEnumParameters(x.Parameters));
        }
    }

    private static void DescribeEnumParameters(IList<IParameter> parameters) {
        if (parameters == null) return;

        foreach (var param in parameters) {
            if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) {
                param.Description += DescribeEnum(nbParam.Enum);
            } else if (param.Extensions.ContainsKey("enum") && param.Extensions["enum"] is IList<object> paramEnums &&
                paramEnums.Count > 0) {
                param.Description += DescribeEnum(paramEnums);
            }
        }
    }

    private static string DescribeEnum(IEnumerable<object> enums) {
        var enumDescriptions = new List<string>();
        Type type = null;
        foreach (var enumOption in enums) {
            if (type == null) type = enumOption.GetType();
            enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
        }

        return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
    }
}

Then add this to your ConfigureServices method in Startup.cs:

c.DocumentFilter<EnumDocumentFilter>();
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Possible to remove the Enum: Array [ 6 ] which appears below ? – Softlion Jul 12 '18 at 09:21
  • 4
    Great solution, but the extensions in `DescribeEnumParameters` were empty in my project. I had to cast the `param` to `NonBodyParameter` and check the enum there: `if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) { param.Description += DescribeEnum(nbParam.Enum); }` – Rabban Dec 13 '18 at 11:00
  • On my project Extensions is empty too, used the @Rabban solution. – Carlos Beppler Jan 08 '19 at 12:01
  • 1
    @Rabban I updated my code to include that. Can you just verify I put it in the right place? I didn't have this issue. Maybe a newer version changed things. – Gabriel Luci Apr 03 '19 at 16:53
  • @GabrielLuci Confirmed and approved ;) – Rabban Apr 04 '19 at 07:47
  • Solution works great, thanks so much. Any reason why DescribeEnumParameters is static? – Rocklan Dec 22 '19 at 23:27
  • @Rocklan Because it can be :) And static methods are [slightly more efficient](https://stackoverflow.com/q/2142062/1202807) than ones that aren't. – Gabriel Luci Dec 22 '19 at 23:50
  • what value should i put above each value? currently i get error : - $exception {"Type provided must be an Enum.\r\nParameter name: enumType"} System.ArgumentException – developer learn999 Nov 17 '20 at 14:53
25

if anyone is interested i have modified the code to work with

.NET CORE 3 and Swagger V5

    public class SwaggerAddEnumDescriptions : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas.Where(x => x.Value?.Enum?.Count > 0))
        {
            IList<IOpenApiAny> propertyEnums = property.Value.Enum;
            if (propertyEnums != null && propertyEnums.Count > 0)
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            DescribeEnumParameters(pathItem.Operations, swaggerDoc);
        }
    }

    private void DescribeEnumParameters(IDictionary<OperationType, OpenApiOperation> operations, OpenApiDocument swaggerDoc)
    {
        if (operations != null)
        {
            foreach (var oper in operations)
            {
                foreach (var param in oper.Value.Parameters)
                {
                    var paramEnum = swaggerDoc.Components.Schemas.FirstOrDefault(x => x.Key == param.Name);
                    if (paramEnum.Value != null)
                    {
                        param.Description += DescribeEnum(paramEnum.Value.Enum, paramEnum.Key);
                    }
                }
            }
        }
    }

    private Type GetEnumTypeByName(string enumTypeName)
    {
        return AppDomain.CurrentDomain
            .GetAssemblies()
            .SelectMany(x => x.GetTypes())
            .FirstOrDefault(x => x.Name == enumTypeName);
    }

    private string DescribeEnum(IList<IOpenApiAny> enums, string proprtyTypeName)
    {
        List<string> enumDescriptions = new List<string>();
        var enumType = GetEnumTypeByName(proprtyTypeName);
        if (enumType == null)
            return null;

        foreach (IOpenApiAny enumOption in enums)
        {
            if (enumOption is OpenApiString @string)
            {
                string enumString = @string.Value;

                enumDescriptions.Add(string.Format("{0} = {1}", (int)Enum.Parse(enumType, enumString), enumString));
            }
            else if (enumOption is OpenApiInteger integer)
            {
                int enumInt = integer.Value;

                enumDescriptions.Add(string.Format("{0} = {1}", enumInt, Enum.GetName(enumType, enumInt)));
            }
        }

        return string.Join(", ", enumDescriptions.ToArray());
    }
}
Hosam Rehani
  • 490
  • 6
  • 10
  • 2
    This only works when the parameter type is *exactly* enum... not nullable enum, collection of enums etc. Check my answer for those cases. – Matyas Apr 20 '20 at 07:58
  • When I run this code I find that enumOption is of type OpenApiString in DescribeEnum – Kirsten Sep 08 '20 at 18:20
  • 1
    Your solution is working, only if I change `GetEnumTypeByName` FirstOfDefaultCondition to this `.FirstOrDefault(x => x.FullName == enumTypeName || x.Name == enumTypeName);` – Yaroslav Bres Oct 22 '20 at 15:23
  • Weirdly - for my enums: swaggerDoc.Components.Schemas.Where(x => x.Value?.Enum?.Count > 0). Enum is actually an empty array... – Mark Walsh Jul 27 '22 at 10:56
22

ASP.NET Core 6 / ASP.NET Core 7

In your program.cs:

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

Please also note:

DescribeAllEnumsAsStrings is obsolete

Julian
  • 33,915
  • 22
  • 119
  • 174
18

This is not possible with standard OpenAPI. Enums are described only with their string values.

Fortunately you can do it with some non-standard extensions that are utilized by your client generator.

NSwag supports x-enumNames

AutoRest supports x-ms-enum.

Openapi-generator supports x-enum-varnames

Other generators might support one of these extensions or have their own.

To generate x-enumNames for NSwag create the following schema filter:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            var array = new OpenApiArray();
            array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n)));
            // NSwag
            schema.Extensions.Add("x-enumNames", array);
            // Openapi-generator
            schema.Extensions.Add("x-enum-varnames", array);
        }
    }
}

And register it as:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
});
norekhov
  • 3,915
  • 25
  • 45
16

With asp.net core 3

using System.Text.Json.Serialization;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
         services.AddControllers().AddJsonOptions(options =>
             options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

But it seems that Swashbuckle Version 5.0.0-rc4 is not ready to support that. So we need to use an option(deprecated) in the Swashbuckle config file until it supports and reflects it like Newtonsoft library.

public void ConfigureServices(IServiceCollection services)
{ 
      services.AddSwaggerGen(c =>
      {
            c.DescribeAllEnumsAsStrings();

The difference between this answer and other answers is using only the Microsoft JSON library instead of Newtonsoft.

Bashir Momen
  • 1,461
  • 19
  • 17
10

I have found nice workaround Here:

@PauloVetor - solved it using ShemaFilter like this:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            model.Enum.Clear();
            Enum.GetNames(context.Type)
                .ToList()
                .ForEach(n =>
                {
                    model.Enum.Add(new OpenApiString(n)); 
                    model.Type = "string";
                    model.Format = null;
                });
        }
    }
}

And in Startup.cs:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
}

Note: use this solution when enums are generated as ints into json (StringEnumConverter is not used or cannot be used). Swagger will show enum names, and will pass the string value into the API, and luckily ASP.NET Core can handle enum values as ints and also as strings, where the string value has to be case-insentitive enum name (e.g. for enum Priority.Low, ASP.NET Core accepts string values Low, low, lOw, etc).

xhafan
  • 2,140
  • 1
  • 26
  • 26
helix-nebula
  • 266
  • 4
  • 5
  • You should also ensure that you update the `model.Format` to `"string"` as it will generally be `"int32"`. – lsuarez May 27 '20 at 19:02
  • I had to update the model.Type to "string" for the same reason Isuarez mentioned. – TK-421 May 31 '21 at 17:37
  • Bravo. While I am forced to continue using Newtonsoft.Json, I didn't necessarily need that to be recognized in the API documentation generation, I just needed enums to be represented as strings. Barring a more complicated scenario (like custom newtonsoft enum names), surprisingly this just worked. – Nicholas Petersen Jul 27 '21 at 14:34
  • 2
    few tweaks to make it work: 1- `model.Format = null;` 2- `model.Type = "string";` – Amir Chatrbahr Oct 14 '21 at 01:51
  • and for NET 6 minimal api ? – Kiquenet Jul 06 '22 at 15:08
10

I have modified Hosam Rehani's answer to work with nullable enums and with collection of enums also. The previous answer also works only if a property is named exactly like it's type. All these problems are addressed in the code below.

It works with .net core 3.x and swagger 5.x.

it could be more efficient by not searching for the enum type twice in some cases.

class SwaggerAddEnumDescriptions : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas.Where(x => x.Value?.Enum?.Count > 0))
        {
            IList<IOpenApiAny> propertyEnums = property.Value.Enum;
            if (propertyEnums != null && propertyEnums.Count > 0)
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths)
        {
            DescribeEnumParameters(pathItem.Value.Operations, swaggerDoc, context.ApiDescriptions, pathItem.Key);
        }
    }

    private void DescribeEnumParameters(IDictionary<OperationType, OpenApiOperation> operations, OpenApiDocument swaggerDoc, IEnumerable<ApiDescription> apiDescriptions, string path)
    {
        path = path.Trim('/');
        if (operations != null)
        {
            var pathDescriptions = apiDescriptions.Where(a => a.RelativePath == path);
            foreach (var oper in operations)
            {
                var operationDescription = pathDescriptions.FirstOrDefault(a => a.HttpMethod.Equals(oper.Key.ToString(), StringComparison.InvariantCultureIgnoreCase));
                foreach (var param in oper.Value.Parameters)
                {
                    var parameterDescription = operationDescription.ParameterDescriptions.FirstOrDefault(a => a.Name == param.Name);
                    if (parameterDescription != null && TryGetEnumType(parameterDescription.Type, out Type enumType))
                    {
                        var paramEnum = swaggerDoc.Components.Schemas.FirstOrDefault(x => x.Key == enumType.Name);
                        if (paramEnum.Value != null)
                        {
                            param.Description += DescribeEnum(paramEnum.Value.Enum, paramEnum.Key);
                        }
                    }
                }
            }
        }
    }

    bool TryGetEnumType(Type type, out Type enumType)
    {
        if (type.IsEnum)
        {
            enumType = type;
            return true;
        }
        else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            var underlyingType = Nullable.GetUnderlyingType(type);
            if (underlyingType != null && underlyingType.IsEnum == true)
            {
                enumType = underlyingType;
                return true;
            }
        }
        else
        {
            Type underlyingType = GetTypeIEnumerableType(type);
            if (underlyingType != null && underlyingType.IsEnum)
            {
                enumType = underlyingType;
                return true;
            }
            else
            {
                var interfaces = type.GetInterfaces();
                foreach (var interfaceType in interfaces)
                {
                    underlyingType = GetTypeIEnumerableType(interfaceType);
                    if (underlyingType != null && underlyingType.IsEnum)
                    {
                        enumType = underlyingType;
                        return true;
                    }
                }
            }
        }

        enumType = null;
        return false;
    }

    Type GetTypeIEnumerableType(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            var underlyingType = type.GetGenericArguments()[0];
            if (underlyingType.IsEnum)
            {
                return underlyingType;
            }
        }

        return null;
    }

    private Type GetEnumTypeByName(string enumTypeName)
    {
        return AppDomain.CurrentDomain
            .GetAssemblies()
            .SelectMany(x => x.GetTypes())
            .FirstOrDefault(x => x.Name == enumTypeName);
    }

    private string DescribeEnum(IList<IOpenApiAny> enums, string proprtyTypeName)
    {
        List<string> enumDescriptions = new List<string>();
        var enumType = GetEnumTypeByName(proprtyTypeName);
        if (enumType == null)
            return null;

        foreach (OpenApiInteger enumOption in enums)
        {
            int enumInt = enumOption.Value;

            enumDescriptions.Add(string.Format("{0} = {1}", enumInt, Enum.GetName(enumType, enumInt)));
        }

        return string.Join(", ", enumDescriptions.ToArray());
    }
}

to use the filter add c.DocumentFilter<SwaggerAddEnumDescriptions>(); to swagger configuration in Startup.cs.

Matyas
  • 1,122
  • 5
  • 23
  • 29
9

For .NET core 5 it is same as .NET core 3.1 which is to add

   options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());

Example:

services.AddControllers(options =>
{
    options.ReturnHttpNotAcceptable = true;
    var builder = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
    options.Filters.Add(new AuthorizeFilter(builder.Build()));
 }).AddJsonOptions(options =>
 {
    options.JsonSerializerOptions.IgnoreNullValues = true;
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
 });
Xavier John
  • 8,474
  • 3
  • 37
  • 51
  • In .NET core 5, the `.Converters.Add(..)` approach does not work for me, while the `[JsonConverter(typeof(StringEnumConverter))]` without having any other settings. Any idea why? – Askolein Feb 18 '21 at 01:16
9

Simple Solution. It works for me.

   using System.Text.Json.Serialization;
    
    
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum Priority
    {
        Low, 
        Medium,
        High
    }
Uzair
  • 317
  • 4
  • 7
  • 1
    Is there any global option instead of decorating POCOs with Json converter attributes? How does it appear in swagger doc? Does it generate Enum on the client side code generation as well? – Maulik Modi May 30 '21 at 06:17
  • @MaulikModi In Swagger doc, it changes the type to string and provides a list of available values. – vahid Oct 10 '21 at 07:04
  • This was actually what worked with NSwagStudio export – Ulterior Oct 25 '21 at 08:00
7

To display the enums as strings in swagger, please configure the JsonStringEnumConverter by adding the following line in ConfigureServices :

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

If you want to display the enums as strings and int values, you could try to create an EnumSchemaFilter to change the schema, as below:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            model.Enum.Clear();
            Enum.GetNames(context.Type)
                .ToList()
                .ForEach(name => model.Enum.Add(new OpenApiString($"{Convert.ToInt64(Enum.Parse(context.Type, name))} = {name}")));
        }
    }
}

Configure the SwaggerGen to use the above SchemaFilter :

services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Version = "v1",
                Title = "ToDo API",
                Description = "A simple example ASP.NET Core Web API",
                TermsOfService = new Uri("https://example.com/terms"),
                Contact = new OpenApiContact
                {
                    Name = "Shayne Boyer",
                    Email = string.Empty,
                    Url = new Uri("https://twitter.com/spboyer"),
                },
                License = new OpenApiLicense
                {
                    Name = "Use under LICX",
                    Url = new Uri("https://example.com/license"),
                }
            });
              
            c.SchemaFilter<EnumSchemaFilter>();
        });
AFract
  • 8,868
  • 6
  • 48
  • 70
mojgan kiyani
  • 91
  • 1
  • 4
  • 1
    Thank you, this works fine and is exactly what i was looking for. But it rises a new issue. The example JSON value of the request in the Swagger UI, shows the required enum as defined in the EnumSchemaFilter which results in: { ...."language": "en(1)",...}. The "Try it out" and then 'execute' button won't work whit this adaption. Any improvement advises for this issue? – ping Jun 29 '22 at 06:10
  • valid for NET 6 minimal api ? – Kiquenet Jul 06 '22 at 15:09
4

in .net core 3.1 & swagger 5.0.0 :

using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace WebFramework.Swagger
{
    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                var enumValues = schema.Enum.ToArray();
                var i = 0;
                schema.Enum.Clear();
                foreach (var n in Enum.GetNames(context.Type).ToList())
                {
                    schema.Enum.Add(new OpenApiString(n + $" = {((OpenApiPrimitive<int>)enumValues[i]).Value}"));
                    i++;
                }
            }
        }
    }

}

and in Startup.cs :

services.AddSwaggerGen(options =>
            {
                #region  EnumDesc
                options.SchemaFilter<EnumSchemaFilter>();
                #endregion
            });

Result

ehsan rezaee
  • 383
  • 3
  • 9
  • 5
    Down side of this is, that when executing a request, instead of passing only int representation (like 2 for example) of an enum value the API will get the full description as a value (like LogicError = 3), which will fail as a bad request since it is not valid value for the enum. – Matyas Apr 20 '20 at 06:44
  • ApiResult is a response class . validation of enum in imput parameter is another discuese in .core you can use IValidatableObject and implementation : public IEnumerable Validate(ValidationContext validationContext) on object – ehsan rezaee Dec 28 '20 at 11:49
4

If you are using newtonsof.json then use this

using Newtonsoft.Json.Converters;


[JsonConverter(typeof(StringEnumConverter))]
public enum MyEnum
{
    A, B
}

If you are using System.Text.Json.Serialization

using System.Text.Json.Serialization;


[JsonConverter(typeof(JsonStringEnumConverter))]
public enum MyEnum
{
    A, B
}
Rajon Tanducar
  • 308
  • 4
  • 8
4

After searching for answers here, I found the partial solution for the issue with displaying enums in the Schema as [SomeEnumString = 0, AnotherEnumString = 1], but all of the answers related to this are only partially correct as @OhWelp mentioned in one of the comments.

Here's the full solution for .NET core (2, 3, currently working on 6):

    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                model.Enum.Clear();

                var names = Enum.GetNames(context.Type).ToList();

                names.ForEach(name => model.Enum.Add(new OpenApiString($"{GetEnumIntegerValue(name, context)} = {name}")));


                 // the missing piece that will make sure that the new schema will not replace the mock value with a wrong value 
                // this is the default behavior - the first possible enum value as a default "example" value
                model.Example = new OpenApiInteger(GetEnumIntegerValue(names.First(), context));
            }
        }

        private int GetEnumIntegerValue(string name, SchemaFilterContext context) => Convert.ToInt32(Enum.Parse(context.Type, name));
    }

And in the startup/program:

    services.AddSwaggerGen(options =>
        {
            options.SchemaFilter<EnumSchemaFilter>();
        });

EDIT: Refactored the code a bit, if you want to see the original version which is more similar to the rest of the answers please, check the chronology of edits.

Nikolai
  • 555
  • 7
  • 17
4

Go to Program.cs and put the below code:

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()));
Samidjo
  • 2,315
  • 30
  • 37
3

I just did this and it works fine!

Startup.cs

services.AddSwaggerGen(c => {
  c.DescribeAllEnumsAsStrings();
});

Model.cs

public enum ColumnType {
  DATE = 0
}

swagger.json

type: {
  enum: ["DATE"],
  type: "string"
}

I hope this helps you how it helped me!

Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
Rodrigo Béco
  • 47
  • 1
  • 2
2

I got it working for .NET 6 Web API using the following code based from the other answers here:

1 - Create a DocumentFilter

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas)
        {
            var propertyEnums = property.Value.Enum;
            if (propertyEnums is { Count: > 0 })
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        if (swaggerDoc.Paths.Count <= 0)
        {
            return;
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            DescribeEnumParameters(pathItem.Parameters);

            var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put, OperationType.Patch };

            foreach (var operation in pathItem.Operations)
            {
                if (affectedOperations.Contains(operation.Key))
                {
                    DescribeEnumParameters(operation.Value.Parameters);
                }
            }
        }
    }

    private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
    {
        if (parameters == null) return;

        foreach (var param in parameters)
        {
            if (param.Schema.Reference != null)
            {
                var enumType = GetEnumTypeByName(param.Schema.Reference.Id);
                var names = Enum.GetNames(enumType).ToList();

                param.Description += string.Join(", ", names.Select(name => $"{Convert.ToInt32(Enum.Parse(enumType, name))} - {name}").ToList());
            }
        }
    }

    private static Type GetEnumTypeByName(string enumTypeName)
    {
        if (string.IsNullOrEmpty(enumTypeName))
        {
            return null;
        }

        try
        {
            return AppDomain.CurrentDomain
                            .GetAssemblies()
                            .SelectMany(x => x.GetTypes())
                            .Single(x => x.FullName != null
                                      && x.Name == enumTypeName);
        }
        catch (InvalidOperationException e)
        {
            throw new Exception($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
        }
    }

    private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
    {
        var enumType = GetEnumTypeByName(propertyTypeName);

        if (enumType == null)
        {
            return null;
        }

        var parsedEnums = new List<OpenApiInteger>();
        foreach (var @enum in enums)
        {
            if (@enum is OpenApiInteger enumInt)
            {
                parsedEnums.Add(enumInt);
            }
        }

        return string.Join(", ", parsedEnums.Select(x => $"{x.Value} - {Enum.GetName(enumType, x.Value)}"));
    }

}

2 - Add it to your Program.cs file

   services.AddSwaggerGen(config =>
        {

            config.DocumentFilter<SwaggerEnumDocumentFilter>();

        })

Schema Result

Parameter

glitchmich
  • 53
  • 6
1

write code inside Startup.cs

services.AddSwaggerGen(c => {
      c.DescribeAllEnumsAsStrings();
    });
ANJYR
  • 2,583
  • 6
  • 39
  • 60
  • 2
    This option is deprecated in the Swashbuckle. It is recommended to use the ASP.NET Core option and then Swashbuckle can reflect that. – Bashir Momen Oct 29 '19 at 09:03
1

There were a number of shortcomings I found in the other answers for what we were looking for, so I thought I'd supply my own take on this. We're using ASP.NET Core 3.1 with System.Text.Json, but our approach works irrespective of the JSON serializer used.

Our goal was to accept lower-camel-cased enum string values in both the ASP.NET Core API as well as document the same in Swagger. We're currently making use of [DataContract] and [EnumMember], so the approach is to take the lower-camel-cased value from the EnumMember value property and use that across the board.

Our sample enum:

[DataContract]
public class enum Colors
{
  [EnumMember(Value="brightPink")]
  BrightPink,
  [EnumMember(Value="blue")]
  Blue
}

We'll use the EnumMember values in Swashbuckle by using an ISchemaFilter as in the following:

public class DescribeEnumMemberValues : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            schema.Enum.Clear();

            //Retrieve each of the values decorated with an EnumMember attribute
            foreach (var member in context.Type.GetMembers())
            {
                var memberAttr = member.GetCustomAttributes(typeof(EnumMemberAttribute), false).FirstOrDefault();
                if (memberAttr != null)
                {
                    var attr = (EnumMemberAttribute) memberAttr;
                    schema.Enum.Add(new OpenApiString(attr.Value));
                }
            }
        }
    }
}

We're using a third-party NuGet package (GitHub repo) to ensure that this naming scheme is also utilized in ASP.NET Core. Configure it in Startup.cs within ConfigureServices with:

services.AddControllers()
  .AddJsonOptions(opt => opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverterWithAttributeSupport()));

Finally, we need to register our ISchemaFilter in Swashbuckle, so also add the following also in ConfigureServices():

services.AddSwaggerGen(c => {
  c.SchemaFilter<DescribeEnumMemberValues>();
});
Whit Waldo
  • 4,806
  • 4
  • 48
  • 70
  • `GetMembers()` would be better as `GetMembers(BindingFlags.Static | BindingFlags.Public)` to limit to only the actual declared enum properties such as "Blue". I also adapted the "else" case to return the Member.Name if there is no `[EnumMember]` attribute. – user2864740 Mar 26 '20 at 18:23
1

If the version of the swagger were 5.5.x, then you need to:

  1. install: Install-Package Swashbuckle.AspNetCore.Newtonsoft -Version 5.5.0

  2. services.AddSwaggerGenNewtonsoftSupport();

Reference: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft

0

.Net Core 3.0

   using Newtonsoft.Json.Converters;

 services
    .AddMvc(options =>
    {
     options.EnableEndpointRouting = false;
     })
    .AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()))
anıl yıldırım
  • 953
  • 1
  • 10
  • 17
0

ASP NET SOLUTION

In my api docs one enum was still shown as int despite the property being marked with StringEnumConverter. We couldn't afford using the global setting for all enums mentioned above. Adding this line in SwaggerConfig solved the issue:

c.MapType<ContactInfoType>(() => new Schema { type = "string", @enum = Enum.GetNames(typeof(ContactInfoType))});
kurdemol94
  • 358
  • 5
  • 12
0

In order to generate enums with name and value you can use this solution.

Prerequisites:

Register SchemaFilter:

GlobalConfiguration.Configuration
    .EnableSwagger("/swagger", c =>
    {
        c.SchemaFilter<EnumSchemaFilter>();
    });

EnumSchemaFilter:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        var enumProperties = schema.properties?.Where(p => p.Value.@enum != null);
        if(enumProperties != null)
        {
            foreach (var property in enumProperties)
            {
                var array = Enum.GetNames(type.GetProperty(property.Key).PropertyType).ToArray();
                property.Value.vendorExtensions.Add("x-enumNames", array); // NSwag
                property.Value.vendorExtensions.Add("x-enum-varnames", array); // Openapi-generator
            }
        }
    }
}
0

I had some problems to get the answers for Swagger and .NET for version 6.x working. I wanted to continue using integer values for enums, but also display a list of possible values (in a readable format). So here is my modified version (includes parts of some answers), maybe it saves some time for some of you ;)

P.S. There is still some room for improvement, you should also check if the logic of the method "GetEnumTypeByName" fits for you. In my case I wanted to primarily update descriptions only for project internal and unique enums.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Models.Api;
using Swashbuckle.AspNetCore.SwaggerGen;

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas)
        {
            var propertyEnums = property.Value.Enum;
            if (propertyEnums is { Count: > 0 })
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        if (swaggerDoc.Paths.Count <= 0)
        {
            return;
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            DescribeEnumParameters(pathItem.Parameters);

            var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put };

            foreach (var operation in pathItem.Operations)
            {
                if (affectedOperations.Contains(operation.Key))
                {
                    DescribeEnumParameters(operation.Value.Parameters);
                }
            }
        }
    }

    private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
    {
        if (parameters == null) return;

        foreach (var param in parameters)
        {
            if (param.Schema.Reference != null)
            {
                param.Description += DescribeEnum(param.Schema.Reference.Id);
            }
        }
    }

    private static Type GetEnumTypeByName(string enumTypeName)
    {
        if (string.IsNullOrEmpty(enumTypeName))
        {
            return null;
        }

        try
        {
            var projectNamespaceRoot = "MyProject.";

            return AppDomain.CurrentDomain
                            .GetAssemblies()
                            .SelectMany(x => x.GetTypes())
                            .Single(x => x.FullName != null
                                      && x.FullName.StartsWith(projectNamespaceRoot)
                                      && x.Name == enumTypeName);
        }
        catch (InvalidOperationException _)
        {
            throw new ApiException($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
        }
    }

    private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
    {
        var enumType = GetEnumTypeByName(propertyTypeName);

        if (enumType == null)
        {
            return null;
        }

        var parsedEnums = new List<OpenApiInteger>();
        foreach (var @enum in enums)
        {
            if (@enum is OpenApiInteger enumInt)
            {
                parsedEnums.Add(enumInt);
            }
        }

        return string.Join(", ", parsedEnums.Select(x => $"{x} = {Enum.GetName(enumType, x.Value)}"));
    }
}

As already mentioned by others, you have to register this Filter inside your Swagger setup:

services.AddSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo
            {
                // some configuration
            });
              
            options.DocumentFilter<SwaggerEnumDocumentFilter>();
        });
DanielD
  • 326
  • 2
  • 7
0

There is a nuget that handles all the tweaks suggested here. It particularly useful if you do not want to change how enums are serialized (because text might be more brittle than a integer value) but just need more documentation in the Swagger description.

The nuget also works with .NET 7 minimal APIs:

using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
...
builder.Services.ConfigureSwaggerGen(options =>
{
    options.AddEnumsWithValuesFixFilters();
});
Dejan
  • 9,150
  • 8
  • 69
  • 117
-1

In .Net 6 with NSwag and System.Text.Json for me works:

services.AddControllersWithViews()
       .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())) 

and

services.AddOpenApiDocument(configure =>
{
       ...
       configure.GenerateEnumMappingDescription = true;
});

It accepts int-s and strings, generates enums in open-api and .ts client with names and show enums with names in SwaggerUI

export enum PaymentDirection {
    Input = "Input",
    Output = "Output",
}
Maksim
  • 59
  • 1
  • 7