0

I am trying to use NSwag to generate a swagger document for my REST API. My response object contains a property that is an abstract class. I would like the schema to use oneOf for the child classes instead. For example my current output looks like this:

"Response": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
    "description": {
        "nullable": true,
        "status": {
            "nullable": true,
            "oneOf": [
            {
                "$ref": "#/components/schemas/Status"
            }
            ]
        }
    }
},
"Status": {
    "type": "object",
    "discriminator": {
        "propertyName": "descriminator",
        "mapping": {
            "TypeA": "#/components/schemas/TypeA",
            "TypeB": "#/components/schemas/TypeB"
        }
    },
    "x-abstract": true,
    "additionalProperties": false,
    "required": [
        "descriminator"
    ],
    "properties": {
        "id": {
            "type": "string",
            "nullable": true
        }
    }
},
"TypeA": {
    "allOf": [
    {
        "$ref": "#/components/schemas/Status"
    },
    {
        "type": "object",
        "additionalProperties": false,
        "properties": {
            "desc": {
                "type": "string",
                "nullable": true
            }
        }
    }
},
"TypeB": {
    "allOf": [
    {
        "$ref": "#/components/schemas/Status"
    },
    {
        "type": "object",
        "additionalProperties": false,
        "properties": {
            "name": {
                "type": "string",
                "nullable": true
            }
        }
    }
}

I would like instead for the Response object to look something like this:

"Response": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
    "description": {
        "nullable": true,
        "status": {
            "nullable": true,
            "oneOf": [
            {
                "$ref": "#/components/schemas/TypeA",
                "$ref": "#/components/schemas/TypeB"
            }
            ]
        }
    }
}

This document will not be used to generate any code and is instead more useful from a readability perspective. Any guidance on how I can get the output I am looking for? I have looked into custom JsonSchemaGenerators and custom JSchemaGenerationProviders but I'm afraid I have no idea how to start with those. I know schema generation does not have OneOf neatly built into it, so I have no idea how to add it modify the schema generation to use it.

SA3709
  • 192
  • 2
  • 11
  • "My response object contains a property that is an abstract class. " <-- This is indicative of a bad API design. Swagger/OpenAPI is not very expressive (unfortunately), but trying to fight-it by hacking-together workarounds for polymorphism will just cause problems in the long-term. I think you should consider revising your DTO design so you don't need to go to this effort instead. "Prevention is better than cure", etc. – Dai Jul 08 '22 at 22:33
  • That is interesting. Can you please provide me with some documentation or resources describing why it is a bad practice? – SA3709 Jul 08 '22 at 22:47
  • Sure: [for example](https://softwareengineering.stackexchange.com/q/399227/91916) - there was a very popular request for polymorphic serialization to be built-in in .NET [but it was canned](https://github.com/dotnet/runtime/issues/29937) for similar reasons - overall, Java/C#-style inheritance-based polymorphism [is going out-of-fashion](https://boxbase.org/entries/2020/aug/3/case-against-oop/) because it's an inexpressive blunt-instrument that's misused by those who don't know better (e.g. down a shot of _vodak_ every time you see a class abuse inheritance as a substitute for mixins) – Dai Jul 08 '22 at 23:49
  • 1
    ...but the overall point is that _service contracts_ (such as a web-service's endpoints and DTOs in its Swagger/OpenAPI file; or a class library's API design) are much easier to reason-about and handle (esp. in generated code) when the spec/contract/API/ABI is "static" (as in, strictly declarative and built-up from _invariant constraints_), e.g. if `GET /people/me` always returns a `Person` DTO then that's easy to reason about, but if instead it _might_ return either a `Person` object or [a `Dog` object](https://w.wiki/Nkv) it _isn't_: now you have to inspect the response object every time. – Dai Jul 08 '22 at 23:58

0 Answers0