Skip to content
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

Regression in v13 for nullable struct fields. #4428

Closed
Tomius opened this issue May 15, 2023 · 0 comments
Closed

Regression in v13 for nullable struct fields. #4428

Tomius opened this issue May 15, 2023 · 0 comments

Comments

@Tomius
Copy link

Tomius commented May 15, 2023

See the following C# controller:

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using NJsonSchema.Annotations;

namespace SampleProject;

[JsonSchema("UserDefinedStruct")] 
public struct UserDefinedStruct
{
  
}

public class UserDefinedClass
{
    [JsonProperty]
    public readonly UserDefinedStruct NonNullableField;
    
    [JsonProperty]
    public readonly UserDefinedStruct? NullableField;
}


public class ExampleController : Controller
{
    [HttpGet]
    [Route("helloworld")]
    public Task<UserDefinedClass> GetHelloWorld()
    {
        return Task.FromResult(new UserDefinedClass());
    }
}

On this server code, run client generation with nswag run , with the following config:

{
  "runtime": "Default",
  "documentGenerator": {
    "webApiToOpenApi": {
      "controllerNames": [
        "SampleProject.ExampleController"
      ],
      "assemblyPaths": [
        "bin/$(Configuration)/net6.0/SampleProject.dll"
      ]
    }
  }
}

This throws the following exception starting from nswag v13 (the generation works before v13):

 System.ArgumentException: An item with the same key has already been added. Key: SampleProject.UserDefinedStruct
     at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
     at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
     at NJsonSchema.Generation.JsonSchemaResolver.AddSchema(Type type, Boolean isIntegerEnumeration, JsonSchema schema)
     at NJsonSchema.Generation.JsonSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver)
     at NSwag.Generation.OpenApiSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) in /_/src/NSwag.Generation/OpenApiSchemaGenerator.cs:line 38
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](TSchemaType schema, ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](Type type, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate(Type type, JsonSchemaResolver schemaResolver)
     at NSwag.Generation.OpenApiSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) in /_/src/NSwag.Generation/OpenApiSchemaGenerator.cs:line 46
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](TSchemaType schema, ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.GenerateWithReferenceAndNullability[TSchemaType](ContextualType contextualType, Boolean isNullable, JsonSchemaResolver schemaResolver, Action`2 transformation)
     at NSwag.Generation.OpenApiSchemaGenerator.GenerateWithReferenceAndNullability[TSchemaType](ContextualType contextualType, Boolean isNullable, JsonSchemaResolver schemaResolver, Action`2 transformation) in /_/src/NSwag.Generation/OpenApiSchemaGenerator.cs:line 90
     at NJsonSchema.Generation.JsonSchemaGenerator.LoadPropertyOrField(JsonProperty jsonProperty, ContextualAccessorInfo accessorInfo, Type parentType, JsonSchema parentSchema, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.GenerateProperties(Type type, JsonSchema schema, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver)
     at NSwag.Generation.OpenApiSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) in /_/src/NSwag.Generation/OpenApiSchemaGenerator.cs:line 38
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](TSchemaType schema, ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.Generate[TSchemaType](ContextualType contextualType, JsonSchemaResolver schemaResolver)
     at NJsonSchema.Generation.JsonSchemaGenerator.GenerateWithReferenceAndNullability[TSchemaType](ContextualType contextualType, Boolean isNullable, JsonSchemaResolver schemaResolver, Action`2 transformation)
     at NSwag.Generation.OpenApiSchemaGenerator.GenerateWithReferenceAndNullability[TSchemaType](ContextualType contextualType, Boolean isNullable, JsonSchemaResolver schemaResolver, Action`2 transformation) in /_/src/NSwag.Generation/OpenApiSchemaGenerator.cs:line 90
     at NSwag.Generation.Processors.OperationResponseProcessorBase.LoadDefaultSuccessResponse(ParameterInfo returnParameter, String successXmlDescription, OperationProcessorContext context) in /_/src/NSwag.Generation/Processors/OperationResponseProcessorBase.cs:line 281
     at NSwag.Generation.Processors.OperationResponseProcessorBase.ProcessOperationDescriptions(IEnumerable`1 operationDescriptions, ParameterInfo returnParameter, OperationProcessorContext context, String successResponseDescription) in /_/src/NSwag.Generation/Processors/OperationResponseProcessorBase.cs:line
  224
     at NSwag.Generation.Processors.OperationResponseProcessorBase.ProcessResponseTypeAttributes(OperationProcessorContext operationProcessorContext, IEnumerable`1 responseTypeAttributes) in /_/src/NSwag.Generation/Processors/OperationResponseProcessorBase.cs:line 51
     at NSwag.Generation.WebApi.Processors.OperationResponseProcessor.Process(OperationProcessorContext context) in /_/src/NSwag.Generation.WebApi/Processors/OperationResponseProcessor.cs:line 49
     at NSwag.Generation.WebApi.WebApiOpenApiDocumentGenerator.RunOperationProcessors(OpenApiDocument document, Type controllerType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription, List`1 allOperations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) in
   /_/src/NSwag.Generation.WebApi/WebApiOpenApiDocumentGenerator.cs:line 243
     at NSwag.Generation.WebApi.WebApiOpenApiDocumentGenerator.AddOperationDescriptionsToDocument(OpenApiDocument document, Type controllerType, List`1 operations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) in /_/src/NSwag.Generation.WebApi/WebApiOpenApiDocumentGenerator.c
  s:line 211
     at NSwag.Generation.WebApi.WebApiOpenApiDocumentGenerator.GenerateForController(OpenApiDocument document, Type controllerType, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) in /_/src/NSwag.Generation.WebApi/WebApiOpenApiDocumentGenerator.cs:line 199
     at NSwag.Generation.WebApi.WebApiOpenApiDocumentGenerator.GenerateForControllersAsync(IEnumerable`1 controllerTypes) in /_/src/NSwag.Generation.WebApi/WebApiOpenApiDocumentGenerator.cs:line 88
     at NSwag.Commands.Generation.WebApi.WebApiToSwaggerCommand.RunIsolatedAsync(AssemblyLoader assemblyLoader) in /_/src/NSwag.Commands/Commands/Generation/WebApi/WebApiToOpenApiCommand.cs:line 106
     at NSwag.Commands.IsolatedCommandBase`1.IsolatedCommandAssemblyLoader.Run(String commandType, String commandData, String[] assemblyPaths, String[] referencePaths) in /_/src/NSwag.Commands/Commands/IsolatedCommandBase.cs:line 76
     at NSwag.Commands.IsolatedCommandBase`1.RunIsolatedAsync(String configurationFile) in /_/src/NSwag.Commands/Commands/IsolatedCommandBase.cs:line 61
     at NSwag.Commands.IsolatedSwaggerOutputCommandBase`1.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/IsolatedSwaggerOutputCommandBase.cs:line 51
     at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 275
     at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 81
     at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 85
     at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 32
     at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
     at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
     at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61

The relevant code simpilified:

if (!dictionary.ContainsKey(typeof(Nullabe<UserDefinedStruct>))) 
{
    dictionary.Add(typeof(UserDefinedStruct), schema);
}

The problem is that the ContainsKey argument's type is different than the one actually added in case of nullable structs.
At some point the UserDefinedStruct is already in the Dictionary, but the Nullabe<UserDefinedStruct> type isn't.
The condition notices that the Nullabe<UserDefinedStruct> isn't there, so then it tries to add UserDefinedStruct , which throws an exception, because it's already in the Dictionary.

Tomius added a commit to criteo-forks/NJsonSchema that referenced this issue Sep 8, 2023
Tomius added a commit to criteo-forks/NJsonSchema that referenced this issue Sep 26, 2023
@Tomius Tomius closed this as completed Oct 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant