-
Notifications
You must be signed in to change notification settings - Fork 13
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
Making oneOf/anyOf schema evaluation easier by discriminator value #34
Comments
Here's one possible way to do this, example adapted from https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/ First we create a We also override the from typing import Mapping
from jschon import JSON, JSONPointer, JSONSchema, URI, create_catalog
from jschon.json import JSONCompatible
from jschon.jsonschema import Result
from jschon.vocabulary import Keyword
from jschon.vocabulary.applicator import AnyOfKeyword, OneOfKeyword
class DiscriminatorKeyword(Keyword):
key = 'discriminator'
depends_on = 'anyOf', 'oneOf',
instance_types = 'object',
def __init__(self, parentschema: JSONSchema, value: Mapping[str, JSONCompatible]):
super().__init__(parentschema, value)
self.any_of_targets = {
JSONPointer.parse_uri_fragment(URI(subschema['$ref'].value).fragment)[-1]:
(index, subschema)
for index, subschema in enumerate(parentschema['anyOf'])
}
def evaluate(self, instance: JSON, result: Result) -> None:
discriminator_property = self.json['propertyName'].value
discriminator = instance[discriminator_property].value
target_index, target_schema = self.any_of_targets[discriminator]
with result(instance, str(target_index)) as subresult:
if not target_schema.evaluate(instance, subresult).passed:
result.fail(f'The instance is invalid against the {discriminator} subschema')
class DiscriminatedAnyOfKeyword(AnyOfKeyword):
key = 'anyOf'
def evaluate(self, instance: JSON, result: Result) -> None:
if not self.parentschema.get('discriminator'):
super().evaluate(instance, result)
class DiscriminatedOneOfKeyword(OneOfKeyword):
key = 'oneOf'
def evaluate(self, instance: JSON, result: Result) -> None:
if not self.parentschema.get('discriminator'):
super().evaluate(instance, result)
catalog = create_catalog('2020-12')
metaschema = catalog.get_schema(URI('https://json-schema.org/draft/2020-12/schema'))
metaschema.kwclasses['anyOf'] = DiscriminatedAnyOfKeyword
metaschema.kwclasses['oneOf'] = DiscriminatedOneOfKeyword
metaschema.kwclasses['discriminator'] = DiscriminatorKeyword
schema = JSONSchema({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schema",
"anyOf": [
{"$ref": "#/$defs/simpleObject"},
{"$ref": "#/$defs/complexObject"}
],
"discriminator": {
"propertyName": "objectType"
},
"$defs": {
"simpleObject": {
"properties": {
"objectType": {"const": "simpleObject"},
"value": {"type": ["number", "string"]}
}
},
"complexObject": {
"properties": {
"objectType": {"const": "complexObject"},
"value": {"type": ["array", "object"]}
}
}
}
})
valid_simple_object = JSON({
"objectType": "simpleObject",
"value": 10
})
invalid_simple_object = JSON({
"objectType": "simpleObject",
"value": ["nope"]
})
valid_complex_object = JSON({
"objectType": "complexObject",
"value": ["super", "complex"]
})
invalid_complex_object = JSON({
"objectType": "complexObject",
"value": None
})
print(schema.evaluate(valid_simple_object).output('basic'))
print(schema.evaluate(invalid_simple_object).output('basic'))
print(schema.evaluate(valid_complex_object).output('basic'))
print(schema.evaluate(invalid_complex_object).output('basic')) I've not tried to handle the Let me know how you get along with this and if you have any questions about the example code. |
In theory (at least as of OAS 3.x) There is also the use of
|
I am having a use case where I have a large number of schemas in a oneOf/anyOf schema. In such a case it would be easier if I could use something like openapi discriminator to hint which schema to choose. Is there anyway I can customise the validation so that whenever there is a discriminator, the normal oneOf/anyOf validation does not happen and the validation happens based on the discriminator mapping?
The text was updated successfully, but these errors were encountered: