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

'Inheritance and Polymorphism' examples, discriminator usage #348

Open
zorba128 opened this issue May 15, 2024 · 1 comment
Open

'Inheritance and Polymorphism' examples, discriminator usage #348

zorba128 opened this issue May 15, 2024 · 1 comment

Comments

@zorba128
Copy link

zorba128 commented May 15, 2024

I'm actually trying to fix openapi/asyncapi generation for my project, and got stuck a bit looking at examples in the Inheritance and Polymorphism section.

First pls take a look at following simple example

  "$defs" : {
    "Apple" : {
      "title" : "Apple",
      "type" : "object",
      "required" : [
        "weight",
        "isSweet",
        "name"
      ],
      "properties" : {
        "weight" : {
          "type" : "number",
          "format" : "double"
        },
        "isSweet" : {
          "type" : "boolean"
        },
        "name" : {
          "type" : "string"
        }
      }
    },
    "Plum" : {
      "title" : "Plum",
      "type" : "object",
      "required" : [
        "weight",
        "name"
      ],
      "properties" : {
        "weight" : {
          "type" : "number",
          "format" : "double"
        },
        "name" : {
          "type" : "string"
        }
      }
    }
  },
  "title" : "Fruit",
  "oneOf" : [
    {
      "$ref" : "#/$defs/Apple"
    },
    {
      "$ref" : "#/$defs/Plum"
    }
  ],
  "discriminator" : {
    "propertyName" : "name",
    "mapping" : {
      "apple" : "#/$defs/Apple",
      "plum" : "#/$defs/Plum"
    }
  }
}

Now pls take a look at Apple schema.

Note this is not schema of Apple itself. This is schema of Apple in context of Fruit coproduct.
It contains name property, that is needed only for apples stored next to other fruits.
But note this schema alone is incomplete. All apples in the fruits basket should have {"name": "apple"} property set.
Apple that has any other name is invalid. Shouldn't Apple schema not only define the name property, but also restrict it to one and only one possible value?

Note - if one does it like that - oneOf + const for name property is perfectly enough to represent Fruit, there is no need to use discriminant at all. And it would then make three representations of polymorphism possible in same way:

  • with product nested in additional property: {"plum":{"weight":1.0}}
  • with additional discriminator field bound to constant value: {"name": "plum", "weight":1.0}
  • with all possible products having incompatible schemas - no discriminant/nesting is needed at all:
    {"weight":1.0}, {"currantColor":"red"}

All those boil up to having specialized schemas that are incompatible with each other (what either happens naturally, or requires some additional work like additional nesting/discriminator), and oneOf on top of that.
And specification is local - it is enough to know Apple schema to produce valid apple fruit entry.

Versus approach with discriminant only, and no const restriction, where one needs schemas both for Apple and its parent Fruit in order to know what to fill into the name property.

Now - if suggestion above is followed you get few nice features for free. Apart from Apple schema becoming standalone, self describing and producing valid Apple fruits, the whole discriminant becomes optional. If provided - it is just optimization hint, allowing quick lookup of target schema to parse/validate data against, so that one does not have to iterate over all the elements of oneOf enumeration.

This ticket is bit reworded discussion from issue reported for Tapir project.

So my suggestions are

  • add specific const restriction to objectType property (can be commented as actually optional, but encouraged)
  • reword example to first illustrate polymorphism with const alone, and then explain how discriminator can be used to improve codec/validator performance.

marcin

@zorba128
Copy link
Author

Also note what would happen if specification would not only require presence of discriminator field in product schemas, but also require those to be constrained with const. In that case the whole part of specs related to discriminator mappings would become unnecessary. No need to specify mappings, no need to assume "schema name is used by default".
Discriminator could just simply point at name of property, and validator/codec generator can actually discover actual mapping just looking at discriminator name, and traversing schemas listed in oneOf.

I think this would simplify specs alot.

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