Nullable Enums don't appear as "nullable: true" in swagger.json

See original GitHub issue

I am running .NET 6.0 ASP.Net Core Web Api with SwashBuckle.AspNetCore version 6.3.0 (currently last stable)

The swagger.json doesn’t show nullable: true where there is a nullable enum in a dto class

{ "openapi": "3.0.1", "info": { "title": "TestBug", "version": "1.0" }, "paths": { "/api/My": { "get": { "tags": [ "My" ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/MyDTO" } }, "application/json": { "schema": { "$ref": "#/components/schemas/MyDTO" } }, "text/json": { "schema": { "$ref": "#/components/schemas/MyDTO" } } } } } } } }, "components": { "schemas": { "MyDTO": { "type": "object", "properties": { "myEnum": { "$ref": "#/components/schemas/MyEnum" }, "myNullableEnum": { "$ref": "#/components/schemas/MyNullableEnum" }, "myNullableInt": { "type": "integer", "format": "int32", "nullable": true } }, "additionalProperties": false }, "MyEnum": { "enum": [ 0, 1, 2 ], "type": "integer", "format": "int32" }, "MyNullableEnum": { "enum": [ 0, 1, 2 ], "type": "integer", "format": "int32" } } } }

Steps to reproduce:

image image image

I am aware of using “UseAllOfToExtendReferenceSchemas”, but it’s not suitable in my case, since it’s breaking other logics.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
domaindrivendevcommented, Nov 15, 2022

Closing as the solution (i.e. to use UseAllOfToExtendReferenceSchemas) has already been provided. If that’s breaking client generators then you should create an issue with them instead of SB.

You could also try UseInlineDefinitionsForEnums as suggested by @anoordover

1reaction
vadymberkut-unicreocommented, May 18, 2023

Solution that worked for me.

/// <summary>
    /// Updates already generated schema by marking $ref enum properties in it as nullable 
    /// if the orginal class property is of type nullable enum type.
    /// NB: schema filter can modify Schemas after they're initially generated.
    /// </summary>
    public class NullableEnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            var isReferenceType =
                TypeHelper.IsReference(context.Type) &&
                !TypeHelper.IsCLR(context.Type) &&
                !TypeHelper.IsMicrosoft(context.Type);
            if(!isReferenceType) { return; }

            var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
            var members = context.Type.GetFields(bindingFlags).Cast<MemberInfo>()
                .Concat(context.Type.GetProperties(bindingFlags))
                .ToArray();
            var hasNullableEnumMembers = members.Any(x => TypeHelper.IsNullableEnum(x.GetMemberType()));
            if (!hasNullableEnumMembers) { return; }

            schema.Properties.Where(x => !x.Value.Nullable).ForEach(property =>
            {
                var name = property.Key;
                var possibleNames = new string[]
                {
                    name,
                    TextCaseHelper.ToPascalCase(name),
                }; // handle different cases
                var sourceMember = possibleNames
                    .Select(n => context.Type.GetMember(n, bindingFlags).FirstOrDefault())
                    .Where(x => x != null)
                    .FirstOrDefault();
                if (sourceMember == null) { return; }

                var sourceMemberType = sourceMember.GetMemberType();
                if (sourceMemberType == null || !TypeHelper.IsNullableEnum(sourceMemberType)) { return; }

                // manual nullability fixes
                if (property.Value.Reference != null)
                {
                    // option 1 - OpenAPI 3.1
                    // https://stackoverflow.com/a/48114924/5168794
                    //property.Value.AnyOf = new List<OpenApiSchema>()
                    //{
                    //    new OpenApiSchema
                    //    {
                    //        Type = "null",
                    //    },
                    //    new OpenApiSchema
                    //    {
                    //        Reference = property.Value.Reference,
                    //    },
                    //};
                    // property.Value.Reference = null;

                    // option 2 - OpenAPI 3.0
                    // https://stackoverflow.com/a/48114924/5168794
                    property.Value.Nullable = true;
                    property.Value.AllOf = new List<OpenApiSchema>()
                    {
                        new OpenApiSchema
                        {
                            Reference = property.Value.Reference,
                        },
                    };
                    property.Value.Reference = null;

                    // option 3 - OpenAPI 3.0
                    // https://stackoverflow.com/a/23737104/5168794
                    //property.Value.OneOf = new List<OpenApiSchema>()
                    //{
                    //    new OpenApiSchema
                    //    {
                    //        Type = "null",
                    //    },
                    //    new OpenApiSchema
                    //    {
                    //        Reference = property.Value.Reference,
                    //    },
                    //};
                    //property.Value.Reference = null;
                }
            });
        }
    }
public static class TypeHelper
    {
        /// <summary>
        /// Checks if type is CLR type.
        /// </summary>
        public static bool IsCLR(Type type) => type.Assembly == typeof(int).Assembly;

        /// <summary>
        /// Checks if type is Microsoft type.
        /// </summary>
        public static bool IsMicrosoft(Type type) => type.Assembly.FullName?.StartsWith("Microsoft") ?? false;

        /// <summary>
        /// Checks if type is value type.
        /// </summary>
        public static bool IsValue(Type type) => type.IsValueType;

        /// <summary>
        /// Checks if type is reference type.
        /// </summary>
        public static bool IsReference(Type type) => !type.IsValueType && type.IsClass;

        /// <summary>
        /// Checks if property type is nullable reference type.
        /// NB: Reflection APIs for nullability information are available from .NET 6 Preview 7.
        /// </summary>
        public static bool IsNullableReferenceProperty(PropertyInfo property) => 
            new NullabilityInfoContext().Create(property).WriteState is NullabilityState.Nullable;

        /// <summary>
        /// Checks if type is enum type.
        /// </summary>
        public static bool IsEnum(Type type) => type.IsEnum || (Nullable.GetUnderlyingType(type)?.IsEnum ?? false);

        /// <summary>
        /// Checks if type is nullable enum type.
        /// </summary>
        public static bool IsNullableEnum(Type type) => Nullable.GetUnderlyingType(type)?.IsEnum ?? false;

        /// <summary>
        /// Checks if type is not nullable enum type.
        /// </summary>
        public static bool IsNotNullableEnum(Type type) => IsEnum(type) && !IsNullableEnum(type);
    }

Based on:

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Nullable property is not presented in the Swashbuckle. ...
And seems nullable enum is not presented in the schema properly. Does anyone have any idea how can I fix this? Best. c#...
Read more >
Enums
You can use the enum keyword to specify possible values of a request parameter or a model property. ... Using nullable: true alone...
Read more >
Nullable Support in Swashbuckle - Jeno's Developer Diary
The biggest issue for me is that Swagger 2.0 doesn't support nullable types for model properties and enums can have only numeric values...
Read more >
Openapi enum null values
I want to have enums that can have null values. ... 'The Type of the Item' type: string nullable: true enum: - "PHYSICAL"...
Read more >
Schema generation rules · GitBook
Loosely speaking, a swagger schema corresponds to a JSONSchema-draft4 schema ... Notice that setting x-nullable: true in such an alias will not render...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found