Supporting x-nullable in openapi 2.0 validation

21 views
Skip to first unread message

Dan Davis

unread,
Dec 4, 2018, 3:37:19 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
I have updated to generating OpenAPI 2.0 definitions with drf-yasg 1.11.1, and with that has come object definitions with properties of this sort:

"index_source": {
    "title": "Index source",
    "type": "integer",
    "x-nullable": true
},

With a previous version, I got out of this by simply saying that strings could also be None:

            types = {
                'string': (str, type(None)),
            }
            self._validator = jsonschema.Draft4Validator(self.swagger_spec, types=types)


Now, I no longer feel like that is the correct thing to do - I think I should instead honor x-nullable, but I'm not sure how to build a custom validator for that.

Julian Berman

unread,
Dec 4, 2018, 3:50:45 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
Hey!

You'd use jsonschema.validators.extend, though that's quite an unfortunate way of representing what I assume that means. You'll need to in this case be replacing the `type` validator, with one that first checks to see whether `x-nullable` is defined alongside.

Have a look at https://python-jsonschema.readthedocs.io/en/latest/creating/ which is unfortunately a bit bare of examples (patches welcome), but what you need to do should be fairly straightforward.

(It'll look like `ExtendedValidator = jsonschema.validators.extend(Draft4Validator, validators={"type": nullable_type})`, and you'll need to then author the nullable_type function to first check if x-nullable is present and then upcall to Draft4Validator.VALIDATORS["type"] to do the appropriate type validation afterwards).

Hope this helps,
-J

Dan Davis

unread,
Dec 4, 2018, 4:24:45 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
Not too hard::

def xnullable_type(validator, types, instance, schema):
    if instance is None and schema.get('x-nullable', False):
        return
    return type_draft4(validator, types, instance, schema)

SwaggerValidator = validators.extend(Draft4Validator, validators={
    'type': xnullable_type,
})

Gotta say though that OOP is well supported in Python :)   I tried MethodType first...

Julian Berman

unread,
Dec 4, 2018, 4:29:01 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
Not sure what you mean about MethodType (or what you mean to say with your OOP comment, I certainly agree Python's a great language for OOP).

Your code also uses type_draft4, which I *suspect* means you've imported jsonschema._validators.type_draft4, which is quite clearly private :). I gave you the public thing you should be using instead.

-J

Dan Davis

unread,
Dec 4, 2018, 4:42:09 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python

On Tuesday, December 4, 2018 at 4:29:01 PM UTC-5, Julian Berman wrote:
Your code also uses type_draft4, which I *suspect* means you've imported jsonschema._validators.type_draft4, which is quite clearly private :). I gave you the public thing you should be using instead.

-J

Thanks, you are suggesting something like this:

def xnullable_type(validator, types, instance, schema):
    if instance is None and schema.get('x-nullable', False):
        return
    return Draft4Validator.VALIDATORS['type'](validator, types, instance, schema)
 
Is this correct?
 

Julian Berman

unread,
Dec 4, 2018, 4:46:33 PM12/4/18
to Dan Davis, jsonschema - An implementation of JSON Schema for Python
Yep

--
You received this message because you are subscribed to the Google Groups "jsonschema - An implementation of JSON Schema for Python" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jsonschema+...@googlegroups.com.
To post to this group, send email to jsons...@googlegroups.com.

Dan Davis

unread,
Dec 4, 2018, 4:59:53 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python

On Tuesday, December 4, 2018 at 4:46:33 PM UTC-5, Julian Berman wrote:
Yep

Thanks - that is what I shall do.

This illustrates what I mean't about OOP.   The more natural way to do this is call
super().validate_type(...) 
or something like that, or if that fails:

     Draft4Validator.validate_type(self, ...)

I'm not sure I understand the reason for the organization of jsonschema.validators, but I do recognize that it is profoundly different.

MethodType is a way of overriding an instance method on a class that has already been created, sort of like this:

validator.type = MethodType(xnullable_type, validator)

I'm sure you need to support Python 2.7, but even there, there are metaclass decorators. 

Dan Davis

unread,
Dec 4, 2018, 5:39:21 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
For the benefit of the community, with drf_yasg 1.11.2, I needed the following to achieve full validation of my detail responses:

def xnullable_type(validator, types, instance, schema):
    if instance is None and schema.get('x-nullable', False):
        return
    return Draft4Validator.VALIDATORS['type'](validator, types, instance, schema)

def xnullable_enum(validator, types, instance, schema):
    if instance is None and schema.get('x-nullable', False):
        return
    return Draft4Validator.VALIDATORS['enum'](validator, types, instance, schema)

SwaggerValidator = validators.extend(Draft4Validator, validators={
    'type': xnullable_type,
    'enum': xnullable_enum,
})

The benefit of validating the schema through automated tests is that it reached all the way back into a 16-year old database, and many of the assertions I made in my Django models did not agree with the data.  Now my API contract is strong, because I validate the schema with this.

I probably *should* use Draft3, because drf_yasg doesn't generate oneOf and such.   jsonschema directly was much better for this than swagger-parser package, because it provides better feedback when validation fails.   It proved customizable.
 

Julian Berman

unread,
Dec 4, 2018, 5:42:28 PM12/4/18
to jsonschema - An implementation of JSON Schema for Python
Ah, then you don't mean OOP, you mean inheritance, which is a thing often found in languages people call OOP but isn't OOP itself :)

Yes, jsonschema intentionally avoids inheritance and its many flaws. In this case all you're using is a simple factory function that returns a new class, and a way you can access implementations of the functions for each draft. Inheriting a validator is totally unsupported (it's not intentionally broken at the minute, but there's definitely no reason to do it.) If you want to learn more about what's wrong with inheritance you can have a look at https://www.youtube.com/watch?v=3MNVP9-hglc or https://en.wikipedia.org/wiki/Composition_over_inheritance, though here it's less about composition than about preventing end users (you :) from trying to make use of anything else about the object you're using than what's publicly documented. Your relationship to validator "subclasses" is strictly one directional. Even here there are some flaws (see e.g. https://github.com/Julian/jsonschema/issues/467) but yeah, the point is -- you can still do everything you want, in a safer way than inheritance, and I get to maintain less things :).

(Bolting methods onto objects after they exist is bad manners even when you *are* using a thing that supports inheritance by the way).

-J
Reply all
Reply to author
Forward
0 new messages