RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.72k stars 1.29k forks source link

Support for YAML anchors and aliases #2400

Open timmvonputtkamer opened 5 years ago

timmvonputtkamer commented 5 years ago

I want to use the C# code generator with input a YAML containing anchors and aliases, however the NSwag command line tool exists with an exception: "System.NotSupportedException: Aliases are not supported in JSON", which basically originates from code in the method NSwag.OpenApiYamlDocument.FromYamlAsync.

There, the YAML contents are parsed, converted to JSON and then an OpenAPI document is constructed from this JSON representation:

public static async Task<OpenApiDocument> FromYamlAsync(string data, string documentPath, SchemaType expectedSchemaType, Func<OpenApiDocument, JsonReferenceResolver> referenceResolverFactory)
{
    var deserializer = new DeserializerBuilder().Build();
    var yamlObject = deserializer.Deserialize(new StringReader(data));
    var serializer = new SerializerBuilder()
        .JsonCompatible()
        .Build();

    var json = serializer.Serialize(yamlObject);

    ...
}

I would suggest the following modification, resolving YAML aliases to build a JSON free from anchors & aliases:

public static async Task<OpenApiDocument> FromYamlAsync(string data, string documentPath, SchemaType expectedSchemaType, Func<OpenApiDocument, JsonReferenceResolver> referenceResolverFactory)
{
    var parser = new MergingParser(new Parser(new StringReader(data)));
    var deserializer = new DeserializerBuilder().Build();
    var yamlObject = deserializer.Deserialize(parser);
    var serializer = new SerializerBuilder()
        .JsonCompatible()
        .DisableAliases()
        .Build();

    var json = serializer.Serialize(yamlObject);

    ...
}
bzuidgeest commented 1 year ago

I just encountered the same error. I'm trying to use Visual studio to generate code from the topdesk yaml files for their api.

NSwag.exe openapi2csclient /className:Incident /namespace:TopDesk.Incidents /GenerateExceptionClasses:false /input:incident_specification_3.8.52.yaml /output:obj\incident_specification_3.8.52Client.cs

NSwag.exe openapi2csclient /className:Changes /namespace:Topdesk /input:change_specification_1.24.0.yaml /output:obj\change_specification_1.24.0Client.cs

The yaml files can be found at: https://developers.topdesk.com/explorer/?page=change

I see this is from 2019. Is there any solution now, four years later?

aivascu commented 1 year ago

@bzuidgeest I have encountered the same issue today.

I have added the yaml via the "Add Connected Services" wizard inside VS 2022, and got the same error.

Updating the NSwag.ApiDescription.Client and Microsoft.Extensions.ApiDescription.Client helped me.

martasp commented 1 year ago

I'm getting the same issue: Aliases are not supported in JSON

GenerateNSwagCSharp:
1>  "C:\Users\mainpc\.nuget\packages\nswag.msbuild\13.0.5\build\../tools/Win/NSwag.exe" openapi2csclient /className:testsetset /namespace:testsetsetset /input:C:\Users\mainpc\Downloads\openapi-test.yaml /output:obj\openapi-testClient.cs
1>NSwag command line tool for .NET 4.6.1+ WinX64, toolchain v13.0.5.0 (NJsonSchema v10.0.22.0 (Newtonsoft.Json v11.0.0.0))
1>Visit http://NSwag.org for more information.

1>System.NotSupportedException: Aliases are not supported in JSON
1>   at YamlDotNet.Serialization.EventEmitters.JsonEventEmitter.Emit(AliasEventInfo eventInfo, IEmitter emitter)
1>   at YamlDotNet.Serialization.ObjectGraphVisitors.AnchorAssigningObjectGraphVisitor.Enter(IObjectDescriptor value, IEmitter context)
1>   at YamlDotNet.Serialization.ObjectGraphVisitors.ChainedObjectGraphVisitor.Enter(IObjectDescriptor value, IEmitter context)
1>   at YamlDotNet.Serialization.ObjectGraphVisitors.CustomSerializationObjectGraphVisitor.Enter(IObjectDescriptor value, IEmitter context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseDictionary[TContext](IObjectDescriptor dictionary, IObjectGraphVisitor`1 visitor, Int32 currentDepth, Type keyType, Type valueType, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseObject[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseDictionary[TContext](IObjectDescriptor dictionary, IObjectGraphVisitor`1 visitor, Int32 currentDepth, Type keyType, Type valueType, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseObject[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseDictionary[TContext](IObjectDescriptor dictionary, IObjectGraphVisitor`1 visitor, Int32 currentDepth, Type keyType, Type valueType, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseObject[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseDictionary[TContext](IObjectDescriptor dictionary, IObjectGraphVisitor`1 visitor, Int32 currentDepth, Type keyType, Type valueType, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseObject[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseDictionary[TContext](IObjectDescriptor dictionary, IObjectGraphVisitor`1 visitor, Int32 currentDepth, Type keyType, Type valueType, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.TraverseObject[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor value, IObjectGraphVisitor`1 visitor, Int32 currentDepth, TContext context)
1>   at YamlDotNet.Serialization.ObjectGraphTraversalStrategies.FullObjectGraphTraversalStrategy.YamlDotNet.Serialization.IObjectGraphTraversalStrategy.Traverse[TContext](IObjectDescriptor graph, IObjectGraphVisitor`1 visitor, TContext context)
1>   at YamlDotNet.Serialization.SerializerBuilder.ValueSerializer.SerializeValue(IEmitter emitter, Object value, Type type)
1>   at YamlDotNet.Serialization.Serializer.EmitDocument(IEmitter emitter, Object graph, Type type)
1>   at YamlDotNet.Serialization.Serializer.Serialize(IEmitter emitter, Object graph)
1>   at YamlDotNet.Serialization.Serializer.Serialize(TextWriter writer, Object graph)
1>   at YamlDotNet.Serialization.Serializer.Serialize(Object graph)
1>   at NSwag.OpenApiYamlDocument.<FromYamlAsync>d__3.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.OpenApiYamlDocument.<FromFileAsync>d__5.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.Commands.OutputCommandBase.<ReadSwaggerDocumentAsync>d__5.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.Commands.InputOutputCommandBase.<GetInputSwaggerDocument>d__12.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.<<RunAsync>b__83_0>d.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.<RunAsync>d__83.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.<RunAsync>d__82.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NConsole.CommandLineProcessor.<ProcessSingleAsync>d__12.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at NConsole.CommandLineProcessor.<ProcessAsync>d__11.MoveNext()
1>--- End of stack trace from previous location where exception was thrown ---
1>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
1>   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
1>   at NConsole.CommandLineProcessor.Process(String[] args, Object input)
1>   at NSwag.Commands.NSwagCommandProcessor.Process(String[] args)
1>C:\Users\mainpc\.nuget\packages\nswag.apidescription.client\13.0.5\build\NSwag.ApiDescription.Client.targets(28,5): error MSB3073: The command ""C:\Users\mainpc\.nuget\packages\nswag.msbuild\13.0.5\build\../tools/Win/NSwag.exe" openapi2csclient /className:testsetset /namespace:testsetsetset /input:C:\Users\mainpc\Downloads\openapi-test.yaml /output:obj\openapi-testClient.cs " exited with code -1.
1>Done building project "WebApplication1.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
========== Elapsed 00:01,074 ==========

It is possible to reproduce the bug with openAI open API yaml file: https://github.com/openai/openai-openapi/blob/master/openapi.yaml