Question

Failed to validate ip address in one dict, file API.json is as follows:

{
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "test",
"type": "object",
"properties": {
    "type": {"enum": ["spice", "vnc"]},
    "listen": {
        "type": "string",
        "oneOf": [
            {"format": "ipv4"},
            {"format": "ipv6"}
        ]
    }
},
"additionalProperties": false
}

Codes are as follows:

from jsonschema import Draft3Validator, ValidationError, FormatChecker
import json

if __name__ == '__main__':
    graphics1 = {'type': 'spice', 'listen': '0.0.0.0'}
    graphics2 = {'type': 'vnc', 'listen': '0.0.0.0'}
    graphics3 = {'type': 'abc', 'listen': '0.0.0.0'}
    graphics4 = {'type': 'vnc', 'listen': '777.485.999'}
    graphics5 = {'type': 'vnc', 'listen': 'fe00::0'}
    graphics6 = {'type': 'vnc', 'listen': 'def'}
    graphics7 = {'type': 'vnc', 'listen': 'fe00::0abcdefdefs'}
    s = json.load(open('API.json'))
    validator = Draft3Validator(s, format_checker=FormatChecker())
    for x in range(1, 8):
        try:
            graphics = locals().get('graphics'+str(x))
            validator.validate(graphics)
        except ValidationError:
            print('; '.join(e.message for e in validator.iter_errors(graphics)))

And the prints are these:

'abc' is not one of [u'spice', u'vnc']

Obviously, '777.485.999' ,'def', and 'fe00::0abcdefdefs' are not ip addresses, but the test script doesn't give warnings about them. I found one doc(https://datatracker.ietf.org/doc/html/draft-zyp-json-schema-03) which says about 'ip-address', but not 'ipv4', but it doesn't work either.

[EDIT]: I have Added FormatChecker() for Draft3Validator, but it still isn't working. But as i tried, Draft4Validator is ok. In the doc, i don't find Draft3Valdator not supporting format/ip-address anywhere, it should work.

Was it helpful?

Solution

Got it, it's not because Draft3Validator doesn't support "format/ip-address", but "oneOf", "allOf", "anyOf" and "not". So the API.json should be:

{
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "test",
"type": "object",
"properties": {
    "type": {"enum": ["spice", "vnc"]},
    "listen": {
        "type": "string",
        "format": "ip-address"
    }
},   
"additionalProperties": false
}

See http://json-schema.org/draft-03/schema# and http://json-schema.org/draft-04/schema#

OTHER TIPS

Check out the docs

Format validation is optional, you need a format checker to enable it.

The other thing I have noticed if you are using draft 4 (http://json-schema.org/draft-04/schema#) is it wants

"format": "ip-address" 

to do its validation if you give it

"format": "ipv4"

..it does not validate at all.

The correct way to use format checker is mentioned in the document.[Julian has already mentioned that]

from jsonschema import Draft3Validator, ValidationError, draft3_format_checker

my_schema = {
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "test",
  "type": "object",
  "properties": {
    "listen": {
        "type": "string",
        "format": 'ip-address'
    },
    "type": {"enum": ["spice", "vnc"]}
},
"additionalProperties": False
}

if __name__ == '__main__':
    graphics1 = {'type': 'spice', 'listen': 'def'}
    graphics2 = {'type': 'vnc', 'listen': '127.0.0.1'}

    validator = Draft3Validator(my_schema, format_checker=draft3_format_checker)
    for x in range(1, 3):
        try:
            graphics = locals().get('graphics'+str(x))
            validator.validate(graphics)
        except ValidationError:
            print('; '.join(e.message for e in validator.iter_errors(graphics)))

Output:

'def' is not a 'ip-address'

Next when you are using format you can just use format keyword I mean without type keyword. source look at example in this doc and format section of this doc,

my_schema = {
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "test",
  "type": "object",
  "properties": {
    "listen": {
        "format": 'ip-address'
    },
    "type": {"enum": ["spice", "vnc"]}
},
"additionalProperties": False
}

From your question it looks like you are trying to check both IPv4 and IPv6 but if you go with schema format: 'ip-address only IPv4 are validated.

from jsonschema import Draft3Validator, ValidationError, draft3_format_checker

my_schema = {
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "test",
  "type": "object",
  "properties": {
    "listen": {
        "format": 'ip-address'
    },
    "type": {"enum": ["spice", "vnc"]}
},
"additionalProperties": False
}

if __name__ == '__main__':
    graphics1 = {'type': 'spice', 'listen': 'def'}
    graphics2 = {'type': 'vnc', 'listen': '127.0.0.1'}
    graphics3 = {'type': 'vnc', 'listen': '::1'}  # IPV6 localhost

    validator = Draft3Validator(my_schema, format_checker=draft3_format_checker)
    for x in range(1, 4):
        try:
            graphics = locals().get('graphics'+str(x))
            validator.validate(graphics)
        except ValidationError:
            print('; '.join(e.message for e in validator.iter_errors(graphics)))

Output:

'def' is not a 'ip-address'
'::1' is not a 'ip-address'

Now if you want to check both IPv4 and IPv6 with Draft7(Since the question is old and Draft7 its new have many other options as well). Here is the revised code with handles both IPv4/v6, error.

from jsonschema import draft7_format_checker, Draft7Validator

my_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "test",
  "type": "object",
  "properties": {
    "listen": {
        'oneOf': [
                    {"format": 'ipv4'},
                    {"format": 'ipv6'},
                ],
    },
    "type": {"enum": ["spice", "vnc"]}
},
"additionalProperties": False
}

if __name__ == '__main__':
    test_list = [{'type': 'spice', 'listen': 'def'}, {'type': 'vnc', 'listen': '127.0.0.1'},{'type': 'vnc', 'listen': '::1'}]

    validator = Draft7Validator(my_schema, format_checker=draft7_format_checker)
    for my_json in test_list:
        errors = validator.iter_errors(my_json)
        for i, error in enumerate(errors):
            print(error.message)

        # # If you want to see more detail cause of each error use this
        # # the ValidationError.context attribute can be used to see the sub-errors which caused the failure
        # for i, error in enumerate(errors):
        #     for suberror in sorted(error.context, key=lambda e: e.schema_path):
        #         print(list(suberror.relative_schema_path), suberror.message, sep=", ")

Output:

'def' is not valid under any of the given schemas

and sample output of detail output(which is commented in above snippet)

[0, 'format'], 'def' is not a 'ipv4'
[1, 'format'], 'def' is not a 'ipv6'

error.context : If the error was caused by errors in subschemas, the list of errors from the subschemas will be available on this property. The schema_path and path of these errors will be relative to the parent error. Reference: https://python-jsonschema.readthedocs.io/en/stable/errors/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top