'How to serialize enum as string in Function App running on .net5 with OpenAPI/Swagger?
I am exploring Function App running on .net5
in the new isolated mode. I have HTTP triggered functions that I want to advertise via OpenAPI / Swagger.
To do so, I am using the package Microsoft.Azure.WebJobs.Extensions.OpenApi
in preview (0.7.2) to add the OpenAPI functionality to my Function App.
I am trying to have the enums
to be shown as string
in the OpenAPI page but I can't have it working properly.
Here is the setup in the Program.cs
file:
public static class Program
{
private static Task Main(string[] args)
{
IHost host = new HostBuilder()
.ConfigureAppConfiguration(configurationBuilder =>
{
configurationBuilder.AddCommandLine(args);
})
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder.Services.Configure<JsonSerializerOptions>(options =>
{
options.Converters.Add(new JsonStringEnumConverter());
options.PropertyNameCaseInsensitive = true;
});
})
.ConfigureServices(services =>
{
// Registers any services.
})
.Build();
return host.RunAsync();
}
}
Here is the enum:
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ApprovalContract
{
[EnumMember(Value = "Approved")]
Approved = 1,
[EnumMember(Value = "Rejected")]
Rejected = 2
}
And one of the class that uses it:
public sealed class DeletionResponseContract
{
[JsonPropertyName("approval")]
public ApprovalContract Approval { get; set; }
}
I replaced any references to Newtonsoft.Json
by System.Text.Json
everywhere.
Here is the output in the Swagger page:
Question
How can I serialize enum
as string
instead of int
in the Swagger page with an HTTP triggered Azure Function running on .net5
?
Update
I saw that the JsonStringEnumConverter
's constructor gives the indication to allow integer values:
public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true)
{
this._namingPolicy = namingPolicy;
this._converterOptions = allowIntegerValues ? EnumConverterOptions.AllowStrings | EnumConverterOptions.AllowNumbers : EnumConverterOptions.AllowStrings;
}
I modified my configuration like this, without any success:
builder.Services.Configure<JsonSerializerOptions>(options =>
{
options.Converters.Add(new JsonStringEnumConverter(allowIntegerValues: false));
options.PropertyNameCaseInsensitive = true;
});
Solution 1:[1]
You must implemente ISchemaFilter and set it on AddSwaggerGen. It will generate a better description of your enum items.
builder.Services.AddSwaggerGen(c =>
{
c.SchemaFilter<EnumSchemaFilter>();
});
//your implementation
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}")));
}
}
}
Solution 2:[2]
I was inspired by Murilo's answer, but couldn't get it to work. So here is an alternative solution:
Create a document filter that will:
find all the enum properties in you swagger schemas
then find the matching property in your code using reflection (note: this doesn't take account of namespaces, so if you have multiple classes with the same name it could fail)
update the swagger property with the values from the c# enum
public class EnumDocumentFilter : IDocumentFilter { public void Apply(IHttpRequestDataObject req, OpenApiDocument document) { foreach(var schema in document.Components.Schemas) foreach(var property in schema.Value.Properties) if (property.Value.Enum.Any()) { var schemaType = Assembly.GetExecutingAssembly().GetTypes().Single(t => t.Name == Camel(schema.Key)); var propertyType = schemaType.GetProperty(Camel(property.Key)).PropertyType; property.Value.Enum = Enum.GetNames(propertyType) .Select(name => new OpenApiString(name)) .Cast<IOpenApiAny>() .ToList(); property.Value.Type = "string"; property.Value.Default = property.Value.Enum.First(); property.Value.Format = null; } } private static string Camel(string key) => $"{char.ToUpperInvariant(key[0])}{key[1..]}"; }
Then register that filter in your OpenApiConfigurationOptions
public class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
{
...
public override List<IDocumentFilter> DocumentFilters { get => base.DocumentFilters.Append(new EnumDocumentFilter()).ToList(); }
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Murilo Maciel Curti |
Solution 2 | Alan Hinton |