It seems that Swashbuckle does not implement polymorphism correctly, and I understand the authorโs point of view regarding subclasses as parameters (if the action expects the Animal class and behaves differently, if you call it using the dog object or cat object, then you should have 2 different actions ...) but as return types I consider it correct to return Animal and objects can be of type Dog or Cat.
Therefore, to describe my API and create the correct JSON scheme in accordance with the correct recommendations (remember how I describe the discriminator, if you have your own discriminator, you may need to change this part in particular), I use document and scheme filters following:
SwaggerDocsConfig configuration; ..... configuration.DocumentFilter<PolymorphismDocumentFilter<YourBaseClass>>(); configuration.SchemaFilter<PolymorphismSchemaFilter<YourBaseClass>>(); ..... public class PolymorphismSchemaFilter<T> : ISchemaFilter { private readonly Lazy<HashSet<Type>> derivedTypes = new Lazy<HashSet<Type>>(Init); private static HashSet<Type> Init() { var abstractType = typeof(T); var dTypes = abstractType.Assembly .GetTypes() .Where(x => abstractType != x && abstractType.IsAssignableFrom(x)); var result = new HashSet<Type>(); foreach (var item in dTypes) result.Add(item); return result; } public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) { if (!derivedTypes.Value.Contains(type)) return; var clonedSchema = new Schema { properties = schema.properties, type = schema.type, required = schema.required };
What implements the previous code is indicated here in the section "Models with support for polymorphism." Essentially, it produces something like the following:
{ "definitions": { "Pet": { "type": "object", "discriminator": "petType", "properties": { "name": { "type": "string" }, "petType": { "type": "string" } }, "required": [ "name", "petType" ] }, "Cat": { "description": "A representation of a cat", "allOf": [ { "$ref": "#/definitions/Pet" }, { "type": "object", "properties": { "huntingSkill": { "type": "string", "description": "The measured skill for hunting", "default": "lazy", "enum": [ "clueless", "lazy", "adventurous", "aggressive" ] } }, "required": [ "huntingSkill" ] } ] }, "Dog": { "description": "A representation of a dog", "allOf": [ { "$ref": "#/definitions/Pet" }, { "type": "object", "properties": { "packSize": { "type": "integer", "format": "int32", "description": "the size of the pack the dog is from", "default": 0, "minimum": 0 } }, "required": [ "packSize" ] } ] } } }