Skip to content

Implementation of 'discriminator' in current schema is unable to produce inheritance tree of api models (also defunct serialiser annotation)

For our development we use code generators supplied by OpenApi tools.

Using the current release schema as the input to this tooling we observe unexpected results in the expected api model hierarchy - in fact there is no inheritance of models applied.

For example consider the NetworkService response described by this yaml snippet:

    NetworkService:
      description: Polymorphic Network Services
      discriminator:
        mapping:
          cloud_vc: '#/components/schemas/CloudNetworkService'
          exchange_lan: '#/components/schemas/ExchangeLanNetworkService'
          mp2mp_vc: '#/components/schemas/MP2MPNetworkService'
          p2mp_vc: '#/components/schemas/P2MPNetworkService'
          p2p_vc: '#/components/schemas/P2PNetworkService'
        propertyName: type
      oneOf:
      - $ref: '#/components/schemas/ExchangeLanNetworkService'
      - $ref: '#/components/schemas/P2PNetworkService'
      - $ref: '#/components/schemas/P2MPNetworkService'
      - $ref: '#/components/schemas/MP2MPNetworkService'
      - $ref: '#/components/schemas/CloudNetworkService'
      title: NetworkService

When openapi tooling is applied to this schema, it results in no common base class(or interface) for the schemas(classes) found in the oneOf listing. Instead we observe a class which has an aggregation of fields found in the schemas(classes) in the oneOf listing. There is an attempt by the tooling to apply serialisation annotation (related to Netwonsoft.Json) in order to materialise the polymorphism described in the yaml but it fails as there is no direct linkage of the oneOf listings with the NetworkService schema (ie there is no explicit inheritance of NetworkService with sub-schemas via allOf directives).

The problem has further complication due to there being a default *Partial schema for each expected api model which materialises as base class after the tooling has been applied.

For example:

    ExchangeLanNetworkService:
      allOf:
      - $ref: '#/components/schemas/ExchangeLanNetworkServicePartial'

Most robust Object-Oriented languages would side-step the problem of multiple inheritance (ie not allow it) so in this way it may cause difficulty for tooling to produce a base class - as in this case ExchangeLanNetworkService cannot both be a NetworkService and ExchangeLanNetworkServicePartial so this may be causing an issue with the expected generation of api model inheritance for these schemas(classes).

Our internal investigations has deduced that the OpenApi specification has provided a secondary usage of discriminators for polymorphic class trees - please see here

The first section pertains to the current mechanism utilising oneOf with a discriminator mapping - however further reading of this section highlights a method which correctly outputs polymorphic modes via openapi tooling.

Taken directly from the spec is this yaml snippet which to our understanding can be utilised in place of oneOf + discriminator notation and correctly renders polymorphic models with correct json serialiser attributes applied.

components:
  schemas:
    Pet:
      type: object
      required:
      - petType
      properties:
        petType:
          type: string
      discriminator:
        propertyName: petType
        mapping:
          dog: Dog
          cat: Cat
          lizard: Lizard
    Cat:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Cat`
        properties:
          name:
            type: string
    Dog:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Dog`
        properties:
          bark:
            type: string
    Lizard:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Lizard`
        properties:
          lovesRocks:
            type: boolean

Consider the generated base class Pet of this schema.

    [DataContract]
    [JsonConverter(typeof(JsonSubtypes), "PetType")]
    [JsonSubtypes.KnownSubType(typeof(Cat), "cat")]
    [JsonSubtypes.KnownSubType(typeof(Dog), "dog")]
    [JsonSubtypes.KnownSubType(typeof(Lizard), "lizard")]
    public partial class Pet :  IEquatable<Pet>, IValidatableObject
    {
...        
        [DataMember(Name="petType", EmitDefaultValue=false)]
        public string PetType { get; set; }
...

}

This appears correctly to give us a discriminator that is integrated and usable with the serialisation library (Newtonsoft.Json).

It also results in inheritance applied to classes generated from sub-schema.

    [DataContract]
    public partial class Lizard : Pet,  IEquatable<Lizard>, IValidatableObject{
...
}

We would clearly like to leverage both effective polymorphism of models and integrated (de)serialisation of models with the serialisation library as the above schema -> code example provides us.

Further to note that when we have applied the openapi code-gen tooling to other langauage output (python/java/..) - none have shown an effective inheritance tree of api models.

After this analysis the questions are:

  1. What is the intended usage and inclusion of *Partial schemas in the current release/staging schemas
    • is it not possible to encapsulate the Partial schema and the non-Partial schema in 1 schema
  2. Can it be considered that the current oneOf implementation could be erroneous or not to be in line with the intentions of the maintainers of the schema?
  3. Is the tooling the issue?
  4. How can the open api documentation describe the discriminator usage in 2 different ways and yet produce different outputs after code-gen?

Thanks in advance for your review and response.