New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated JSON deserializer fails at runtime when schema enum contains duplicate keys #800

Open
GeorgDangl opened this Issue Oct 28, 2018 · 2 comments

Comments

Projects
None yet
2 participants
@GeorgDangl
Copy link

GeorgDangl commented Oct 28, 2018

The following is the definition of the System.Net.HttpStatusCode type (shortened) from .nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll:

namespace System.Net
{
    //
    // Summary:
    //     Contains the values of status codes defined for HTTP.
    public enum HttpStatusCode
    {
        //
        // Summary:
        //     Equivalent to HTTP status 300. System.Net.HttpStatusCode.Ambiguous indicates
        //     that the requested information has multiple representations. The default action
        //     is to treat this status as a redirect and follow the contents of the Location
        //     header associated with this response.
        Ambiguous = 300,
        //
        // Summary:
        //     Equivalent to HTTP status 300. System.Net.HttpStatusCode.MultipleChoices indicates
        //     that the requested information has multiple representations. The default action
        //     is to treat this status as a redirect and follow the contents of the Location
        //     header associated with this response.
        MultipleChoices = 300,
    }
}

Per this StackOverflow question (which references the C# spec), this is allowed and correct syntax.

Generating the schema

var jsonSchema = (await NJsonSchema.JsonSchema4.FromTypeAsync(typeof(System.Net.HttpStatusCode))).ToJson();

Produces this (shortened) JSON schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "HttpStatusCode",
  "type": "integer",
  "description": "",
  "x-enumNames": [
    "MultipleChoices",
    "Ambiguous"
  ],
  "enum": [
    300,
    300
  ]
}

After a bit more processing (I'm using a model containing a HttpStatusCode property in a Web API, which gets then converted via NSwag to a Swagger definition, which then again is used to create a C# client) this is the generated C# client code (shortened):

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.11.0.0 (Newtonsoft.Json v9.0.0.0)")]
    public enum HttpStatusCode
    {   
        [System.Runtime.Serialization.EnumMember(Value = "Ambiguous")]
        MultipleChoices = 14,
    
        [System.Runtime.Serialization.EnumMember(Value = "Ambiguous")]
        Ambiguous = 15
    }

This just fails at runtime when deserializing with Newtonsoft.Json:

System.InvalidOperationException : Enum name 'Ambiguous' already exists on enum 'HttpStatusCode'.

The workaround for me is to simply use the System.Net.HttpStatusCode enum in my generated client instead of the generated one.

@RSuter RSuter added the question label Oct 31, 2018

@GeorgDangl

This comment has been minimized.

Copy link

GeorgDangl commented Jan 3, 2019

To mitigate this, the following PostProcess works for me in ASP.NET Core:

services.AddSwaggerDocument(c =>
{
    c.PostProcess = (x) =>
    {
        // Some enum classes use multiple integer values for the same value, e.g.
        // System.Net.HttpStatusCode uses these:
        // RedirectKeepVerb = 307
        // TemporaryRedirect = 307
        // MVC is configured to use the StringEnumConverter, and NJsonSchema errorenously
        // outputs the duplicates. For the example above, the value 'TemporaryRedirect' is
        // serialized twice, 'RedirectKeepVerb' is missing.
        // The following post process action should remove duplicated enums
        // See https://github.com/RSuter/NJsonSchema/issues/800 for more information
        foreach (var enumType in x.Definitions.Select(d => d.Value).Where(d => d.IsEnumeration))
        {
            var distinctValues = enumType.Enumeration.Distinct().ToList();
            enumType.Enumeration.Clear();
            foreach (var distinctValue in distinctValues)
            {
                enumType.Enumeration.Add(distinctValue);
            }
        }
    };
});

I've blogged about it here:
https://blog.dangl.me/archive/remove-duplicate-enum-entries-in-swagger-documents-with-nswag-in-aspnet-core/

@RSuter RSuter added bug and removed question labels Jan 4, 2019

@RSuter RSuter added this to the vNext milestone Jan 4, 2019

@RSuter

This comment has been minimized.

Copy link
Owner

RSuter commented Jan 9, 2019

I think you also need to remove the corresponding name in EnumerationNames… otherwise the name-value mapping might be wrong in the generated code. I think this problem should be fixed directly in NJS... i.e. filter out duplicates as you do it manually now...

@RSuter RSuter added the help wanted label Jan 9, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment