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

Method accepting polymorphic type uses an inheriting type instead of the base type #5081

Open
Rene-Sackers opened this issue Jan 7, 2025 · 0 comments

Comments

@Rene-Sackers
Copy link

Describe the bug

When generating a C# client using NSwag.CodeGeneration.CSharp an endpoint parameter accepting an abstract class, uses a sub-type as its parameter type in the generated C# client.

I've been struggling to solve this since there's been quite a few changes to the whole polymorphism part it seems and documentation is outdated. I'm not sure if anything I'm doing is actually correct (anymore). Is UseOneOfForPolymorphism and UseAllOfToExtendReferenceSchemas still the way to do this? Are the attributes on my base/inheriting models correct?

ASP.NET Project setup:

builder.Services
	.AddControllers()
	.AddNewtonsoftJson();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
	options.UseOneOfForPolymorphism();
	options.UseAllOfToExtendReferenceSchemas();
});

// ...

app.UseSwagger();
app.UseSwaggerUI();

Models:

[JsonConverter(typeof(JsonInheritanceConverter))]
[KnownType(typeof(Dog))]
[KnownType(typeof(Spider))]
public abstract class Animal
{
	public int LegCount { get; set; }
}

public class Dog : Animal
{
	public int TailLength { get; set; }
}

public class Spider : Animal
{
	public bool Venomous { get; set; }
}

Controller:

[HttpPost]
[Route("/Post")]
public void Post(Animal animal)
{
	Console.WriteLine($"Incoming type: {animal.GetType().FullName}");
	Console.WriteLine(JsonConvert.SerializeObject(animal, Formatting.Indented));
}

Client generator settings:

var swaggerJson = File.ReadAllText(swaggerFile.FullName);
var document = await OpenApiDocument.FromJsonAsync(swaggerJson);

var clientProjectNamespace = Assembly.GetExecutingAssembly().FullName
	!.Split(',')[0]
	.Replace(".ClientGenerator", ".Client");

var settings = new CSharpClientGeneratorSettings
{
	CSharpGeneratorSettings =
	{
		Namespace = clientProjectNamespace
	}
};

var generator = new CSharpClientGenerator(document, settings);
var code = generator.GenerateFile();
File.WriteAllText(output.FullName, code);

Generated client:

public virtual System.Threading.Tasks.Task PostAsync(System.Collections.Generic.IEnumerable<Dog> body)

Version used

Package Version
Newtonsoft.Json 13.0.3
NJsonSchema 11.1.0
NJsonSchema.NewtonsoftJson 11.1.0
Swashbuckle.AspNetCore 7.2.0
Swashbuckle.AspNetCore.Swagger 7.2.0
Swashbuckle.AspNetCore.SwaggerGen 7.2.0
Swashbuckle.AspNetCore.SwaggerUI 7.2.0
NSwag.CodeGeneration.CSharp 14.2.0
swashbuckle.aspnetcore.cli 7.2.0

To Reproduce

Repo repro

Expected behavior

I expect the generated method to be:

public virtual System.Threading.Tasks.Task PostAsync(Animal body)

But it's actually;

public virtual System.Threading.Tasks.Task PostAsync(Dog body)

Additional context

I hope I've provided enough info, please let me know if I can clarify anything!

The generated swagger.json is also available in the repo.
I use the dotnet swagger tofile cli to generate it, but I don't think that makes much of a difference. The spec seems to properly specify the polymorphism, and it works for the get method, but not the post method.

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