Open satyanarayan18 opened 6 years ago
Are you using Swashbuckle to generate the Swagger spec?
Yes.
Check your Swagger spec: Is the required information (the actual enum names) even available in the spec? When NSwag generates a Swagger spec, it adds an additional x-enumNames property with all enum names so that the generator knows the names and values... In Swashbuckle I think you only have the option to use string serialization instead of numbers so that the value und names are the same...
I am using "swagger": "2.0" and in the spec I have not found anything x-enumNames.
Could you please guide me what I am doing wrong and let me know any additional information required
The problem is that Swagger does not support number enums with names, this is why we added a custom property x-enumNames to NSwag so that you can do it... but this only works if the generator (like NSwag does) generates this property (Swashbuckle does not). But you can configure Swashbuckle and ASP.NET to serialize enums not as integer but as string so that the enum value also is the name and the code gen works as expected...
Thanks for the quick response.
I have added code to serialize enums as string instead of int services.AddMvc() .AddJsonOptions(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()));
Now I am getting enum with names but still not getting exact enum name as that of name defined in C#
Whats the diff?
export enum ClassATestEnum { Pass="Pass", Fail="Fail" }
but the name is not changing. Even I found that if the same Enum is used in multiple classes then that many enums are generated but not the single one
strange, can you post the part of this enum in the generated swagger spec?
That was just an example but now I am posting the exact code
Enum generated in the spec,
export enum AdministrativeActivityInputDtoCategory { Todo = "Todo", Survey = "Survey", Notification = "Notification", } public class AdministrativeActivityInputDto { public ActivityCategoryEnum Category { get; set; } }
public enum ActivityCategoryEnum { Todo, Survey, Notification }
Hope it helps
I need to see this in the generated Swagger spec?
Are you referring to swagger.json file or proxy file which are generated through nswag
swagger.json - i need to know what Swashbuckle generated...
"AdministrativeActivityInputDto": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": [
"Todo",
"Survey",
"Notification"
]
}
}
And what enum is generated?
export enum AdministrativeActivityInputDtoCategory {
Todo =
Ok looks correct. What’s wrong about that?
Name of enum generated on client side, it is not the mention on C#.
Enum name on C# :- ActivityCategoryEnum Enum name generated :- AdministrativeActivityInputDtoCategory (It is taking the class name with the property name)
So if the same enum is used in multiple classes then multiple enums are generated on client side instead of generating only one enum.
Yes, there is no way to "restore" the original name with the Swashbuckle generated spec because the enum is not referenced and thus does not have a name... How would NSwag restore the name with the given spec?
But don't you think so this way I will be having n number of enums generated if reference in n places. This way client side file size also increases and there by impacting the performance.
Is there any possibility to handle the duplication I mean Can I apply some attribute on the enum to get the exact name and avoid duplication.
If the spec is generated with NSwag it automatically uses references so that it is only generated once and with the correct name - I dont know if this is possible with Swashbuckle.
Ok... Thanks for the update.. Lastly can I get the enum name along with the integer value associated to it like,
Todo = 0, Survey = 1, Notification = 2,
This is what Swashbuckle generates and Swagger originally supports:
String enum:
"category": {
"type": "string",
"enum": [
"Todo",
"Survey",
"Notification"
]
}
Integer enum:
"category": {
"type": "string",
"enum": [
1,
2,
3
]
}
As you can see there is no way to store both information (name and value). This is why NSwag will generate and process the following to store both:
"category": {
"type": "string",
"x-enumNames": [
"Todo",
"Survey",
"Notification"
],
"enum": [
1,
2,
3
]
}
Also NSwag references the enum so that the original name can be restored. I don't know if this is also possible with Swashbuckle.
Where can I see this piece of code
"category": {
"type": "string",
"x-enumNames": [
"Todo",
"Survey",
"Notification"
],
"enum": [
1,
2,
3
]
}
x-enumNames I am not able to find in the generated proxy of nsswag
You currently generate the swagger.json with Swashbuckle, so this x-enumNames is missing...
If you generate the swagger.json with NSwag it will automatically add x-enumNames which will be picked up by the NSwag code generators.
Maybe there is a way to manually add this x-enumNames also with Swashbuckle (a document filter?)
Can I get the example to generate swagger.json using NSwag
e.g. with middlewares like in Swashbuckle https://github.com/RSuter/NSwag/wiki/Middlewares
(there are a lot of other options)
Thanks for the quick response.
Also, I have one more query.
Can I generate multiple swagger.json file from a single api end point?
Is there any way like to place some attribute to distinguish and generate multiple swagger.json file
You can add multiple UseSwagger calls with different routes and a custom document processors (see wiki) to exclude operations
My API's are generated dynamically I am using ABP(ASP.Net Boilerplate) so can you just possible ways to generate the multiple swagger.json file.
In Startup.cs you probably have the UseSwagger registration, you can register multiple with different routes and custom exclusions.
Ok.. will explore more into that option..
If you have any link with examples to show generating multiple swagger.json with single endpoint that would really helpful in context to ABP
If you can't use NSwag to generate the swagger definition, you can extend swashbuckle to include x-enumNames like this:
Do not use "DescribeAllEnumsAsStrings" option in Swaschbuckle. Create a custom SchemaFilter that looks like this:
public class XEnumNamesSchemaFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
var typeInfo = context.SystemType.GetTypeInfo();
if (typeInfo.IsEnum)
{
var names = Enum.GetNames(context.SystemType);
model.Extensions.Add("x-enumNames", names);
}
}
}
Apply it like this:
options.SchemaFilter<Infrastructure.SchemaFilters.XEnumNamesSchemaFilter>();
Swashbuckle willl now generate a swagger definition that NSwag code generators will handle fine.
ah, I figured out why this problem occurred. Both of your two questions #1499 & #1234 are the same reason. It's not the bug of NSwag, it's abp's fault. abp use Swashbuckle.AspNetCore as it's default doc generator in server side, and client use nswag to generate ts code. you should change Swashbuckle.AspNetCore to NSwag.AspNetCore, The problem will be solved.
You need to know the following:
Hi there, we've followed the NSwag Enum docs, but we're having some problems with System.Text.Json
and enums showing up as their int values in the Swagger docs (the enums are being serialised as strings in the json going over the wire). Here is what our Startup looks like
.AddControllers()
.AddJsonOptions(options =>
{
// Serialize enums as strings globally.
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
})
The only solution to our problem at the moment is to annotate all our enum classes with the JsonStringEnumConverter
, but this is a bit of a pain to do for each model
using System.Text.Json.Serialization;
[JsonConverter(typeof(JsonStringEnumConverter))] // Applying this means Swagger displays [Starting, Running], not [1,2]
public enum ImportState
{
Starting = 1,
Running = 2
}
There was a similar issue raised on the Swashbuckle repo, https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1269, is this something that's been seen before with Nswag?
If you are using Newtonsoft.Json, can be fixed like this:
services.AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()));
@FinHorsley In the case of newtonsoft we use the same behavior, i.e. we inspect the serializer settings for this global filter or on the property or enum directly... (not an NSwag config).
In the case of System.Text.Json, we map the JsonStringEnumConverter to the Newtonsoft one, so the behavior should be the same: https://github.com/RicoSuter/NJsonSchema/blob/master/src/NJsonSchema/Generation/SystemTextJsonUtilities.cs#L36 (for System.Text.Json we map the rules to a Newtonsoft contract resolver so that we only need one implementation internally)
If you can't use NSwag to generate the swagger definition, you can extend swashbuckle to include x-enumNames like this:
Do not use "DescribeAllEnumsAsStrings" option in Swaschbuckle. Create a custom SchemaFilter that looks like this:
public class XEnumNamesSchemaFilter : ISchemaFilter { public void Apply(Schema model, SchemaFilterContext context) { var typeInfo = context.SystemType.GetTypeInfo(); if (typeInfo.IsEnum) { var names = Enum.GetNames(context.SystemType); model.Extensions.Add("x-enumNames", names); } } }
Apply it like this:
options.SchemaFilter<Infrastructure.SchemaFilters.XEnumNamesSchemaFilter>();
Swashbuckle willl now generate a swagger definition that NSwag code generators will handle fine.
I wonder if anyone could show how to do this with the latest version....
A version of the XEnumNamesSchemaFilter for the latest version Swashbuckle:
public class XEnumNamesSchemaFilter : ISchemaFilter
{
private const string NAME = "x-enumNames";
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
var typeInfo = context.Type;
// Chances are something in the pipeline might generate this automatically at some point in the future
// therefore it's best to check if it exists.
if (typeInfo.IsEnum && !model.Extensions.ContainsKey(NAME))
{
var names = Enum.GetNames(context.Type);
var arr = new OpenApiArray();
arr.AddRange(names.Select(name => new OpenApiString(name)));
model.Extensions.Add(NAME, arr);
}
}
}
@worthy7 @eluchsinger
@satyanarayan18 it's easier to use own contracts so in AdditionalNamespaceUsages (I'm referring to *.nswag config file for NSwag Studio, in the Studio the name may be slightly diffrent ) add a namespace to your contracts/DTOs project "your contracts/DTOs project" should be deployed to your internal nuget server and your generated client should reference it. the flag GenerateDtoTypes should be set to false. This way you'll use your own enums and own DTOs in auto generated code
A version of the XEnumNamesSchemaFilter for the latest version Swashbuckle:
public class XEnumNamesSchemaFilter : ISchemaFilter { private const string NAME = "x-enumNames"; public void Apply(OpenApiSchema model, SchemaFilterContext context) { var typeInfo = context.Type; // Chances are something in the pipeline might generate this automatically at some point in the future // therefore it's best to check if it exists. if (typeInfo.IsEnum && !model.Extensions.ContainsKey(NAME)) { var names = Enum.GetNames(context.Type); var arr = new OpenApiArray(); arr.AddRange(names.Select(name => new OpenApiString(name))); model.Extensions.Add(NAME, arr); } } }
@worthy7 @eluchsinger
Works like a charm. This should be added to the wiki.
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; //for enums as string names
});
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",
}
@ ramax495 this seems not to work either on 14.0.3 of nswag build.
Hi,
i'm also strugle with this - after update to 14.x and NET 8.
before that, my enums were generated properly - with the correct integer indexes next to it.
after update i get something like this - without StringEnumConverter:
export enum ChronoUserRightsEnum { _0 = 0, _1 = 1, _2 = 2, _3 = 3, _4 = 4, _5 = 5, }
and with EnumConverter something like this:
export enum ChronoUserRightsEnum { Firma = "Firma", Department = "Department", Person = "Person", Filiale = "Filiale", Group = "Group", Hierarchical = "Hierarchical", }
but what i really need would be like (with version 13.x):
export enum ChronoUserRightsEnum { Firma = 0, Department = 1, Person = 2, Filiale = 3, Group = 4, Hierarchical = 5, }
is there a solution to generate the correct way again? any hints or tips would be highly appreciated - thanks
update: i finally figured it out - and it works for now with this workaround: https://stackoverflow.com/a/73872112
C# Code:- public enum Test { Pass, Fail }
public class ClassA { public Test TestEnum {get;set;} }
Client Side Type Script:-
export enum ClassATestEnum { _0=0, _1=1 }