Many people including myself asked for full support of JSON Schema.
But I totally understand why oneOf & anyOf are causing a lot of problems:
It was never intended to use for anything beyond validation.
discriminator was good attempt to solve most of the common use cases.
We tried it with our customers and even have added support for it in our documentation engine.
But we have discovered a few issues that limit usability of discriminator:
discriminator field should match one of the keys in definitions object.discriminator inside multiple payloads you will experience name clashes.a-zA-Z0-9.-_\) it creates a big problem for non-English field values.discriminator.allOfs and it becomes especially problematic when inherited schemas exist in separate files.discriminator. A solution would be to provide translator of discriminator into oneOf and use standard JSON Schema validator afterwards.My proposal is to replace discriminator with a new valueSwitch keyword.
Here is modifiend discriminator example:
definitions:
Pet:
type: object
properties:
name:
type: string
petType:
type: string
required:
- name
- petType
# Proposed keyword
valueSwitch:
# Name of the property which values are used to determinate apropriate subschema
property: petType
# Map of property values on Schema Objects
values:
Cat:
$ref: '#/definitions/CatMixin'
Dog:
$ref: '#/definitions/DogMixin'
# Schema which should be used when property is missing or
# it's value doesn't match any key from `values` object
default:
description: Use specified pet type
properties:
ICZN:
description: International Code of Zoological Nomenclature
type: string
required:
- ICZN
CatMixin:
description: A representation of a cat
type: object
properties:
huntingSkill:
type: string
description: The measured skill for hunting
default: lazy
enum:
- clueless
- lazy
- adventurous
- aggressive
required:
- huntingSkill
DogMixin:
description: A representation of a dog
type: object
properties:
packSize:
type: integer
format: int32
description: the size of the pack the dog is from
default: 0
minimum: 0
required:
- packSize
In the example I also added support for user-defined petType to illustrate usage of default keyword.
And here is how it solves problems mentioned above:
values object.default, see above example.valueSwitch into pure JSON Schema Draft4 using straightforward algorithm, for example:oneOf:
- type: object
required:
- petType
properties:
petType:
enum:
- Cat
allOf:
- $ref: '#/definitions/CatMixin'
- type: object
required:
- petType
properties:
petType:
enum:
- Dog
allOf:
- $ref: '#/definitions/DogMixin'
- properties:
petType:
not:
enum:
- Cat
- Dog
allOf:
description: Use specified pet type
properties:
ICZN:
description: International Code of Zoological Nomenclature
type: string
required:
- ICZN
Moreover it is possible to convert discriminator into valueSwitch for all existing Swagger specs.
I like the basic idea, but wonder how to express the multiple levels of inheritance: how would I specialize dogs into hunting dogs and shepherd dogs? Would I have to "flatten" the inheritance hierarchy by adding members HuntingDog and ShepherdDog to the valueSwitch/values object and provide an array of incremental mixins for them?
valueSwitch:
# Name of the property which values are used to determinate apropriate subschema
property: petType
# Map of property values on Schema Objects
values:
Cat:
$ref: '#/definitions/CatMixin'
Dog:
$ref: '#/definitions/DogMixin'
HuntingDog:
- $ref: '#/definitions/DogMixin'
- $ref: '#/definitions/HuntingDogMixin'
ShepherdDog:
- $ref: '#/definitions/DogMixin'
- $ref: '#/definitions/ShepherdDogMixin'
This feels strange. Strong-typed languages will most likely have a problem with this sort of specification, given that on-the-fly framework conversions will fail. For example what should the request object type actually be? (Or what is the response object type?) Although, it would be nice to be able to specify different validations--i.e. C if A or D if B, but I'm not sure that this is the best approach.
Would I have to "flatten" the inheritance hierarchy by adding members HuntingDog and ShepherdDog to the valueSwitch/values object and provide an array of incremental mixins for them?
@ralfhandl You just need to use allOf for it:
valueSwitch:
# Name of the property which values are used to determinate apropriate subschema
property: petType
# Map of property values on Schema Objects
values:
Cat:
$ref: '#/definitions/CatMixin'
Dog:
$ref: '#/definitions/DogMixin'
HuntingDog:
allOf:
- $ref: '#/definitions/DogMixin'
- $ref: '#/definitions/HuntingDogMixin'
ShepherdDog:
allOf:
- $ref: '#/definitions/DogMixin'
- $ref: '#/definitions/ShepherdDogMixin'
values has normal Schema Objects as values, so you can use everything that Swagger support:
allOf, $ref, type, etc. You can even add nested valueSwitch :smile:
This feels strange. Strong-typed languages will most likely have a problem with this sort of specification, given that on-the-fly framework conversions will fail. For example what should the request object type actually be? (Or what is the response object type?)
@wparad It possible to implement in all typed-languages I know of, most of them natively support polymorphism and the rest support "union"-like types. You can even implement it in pure C, using union type. What's important you always have access to property used for switching, petType in above example. On implementation side it very simple you just add single switch which executed in runtime and it exactly the same as current discriminator.
It would be nice to be able to specify different validations--i.e. C if A or D if B, but I'm not sure that this is the best approach.
In really most of the time developers used only two patterns for dynamic data in JSON:
typeSwitch keyword.To handle exotic cases you need to add something very similar to oneOf and that's clearly not going to happen. So instead of creating some theoretical all in one solution let's solve real-life problems without introducing too much complexity into tooling and the spec itself.
I'd like to suggest another approach to handling the general idea here. How about we get some examples of different modeling challenges and then put together a comprehensive solution from them? Maybe it's been done but It would be great to start linking to them here.
I believe this has already been done quite thoroughly, resulting in the widely accepted json-schema draft 4.
Tackling PR: #741
There have been several developments related to this in more recent drafts of JSON Schema.
draft-07's "if"/"then"/"else" can be used to make switching conditions explicit. You can use those keywords with anyOf or oneOf to implement an n-way switch.
The vocabulary definition and management proposals for draft-08 are primarily intended to allow adding a set of code generation keywords to augment the validation keywords. The two use cases are quite different, which has resulted in very awkward attempts to cram data definition into the validation constraint system. See json-schema-org/json-schema-spec#561 if you want to upvote the vocabulary support proposal.
Note that here in the OAS repo, #1443 is tracking whether/how to support newer drafts.
Most helpful comment
@ralfhandl You just need to use
allOffor it:valueshas normal Schema Objects as values, so you can use everything that Swagger support:allOf,$ref,type, etc. You can even add nestedvalueSwitch:smile:@wparad It possible to implement in all typed-languages I know of, most of them natively support polymorphism and the rest support "union"-like types. You can even implement it in pure C, using
uniontype. What's important you always have access to property used for switching,petTypein above example. On implementation side it very simple you just add singleswitchwhich executed in runtime and it exactly the same as currentdiscriminator.In really most of the time developers used only two patterns for dynamic data in JSON:
typeSwitchkeyword.To handle exotic cases you need to add something very similar to
oneOfand that's clearly not going to happen. So instead of creating some theoretical all in one solution let's solve real-life problems without introducing too much complexity into tooling and the spec itself.