Description
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 toobjectType
property (can be commented as actually optional, but encouraged) - reword example to first illustrate polymorphism with
const
alone, and then explain howdiscriminator
can be used to improve codec/validator performance.
marcin