15

Given this schema:

{
    "type": "object",
    "properties": {
        "account": {
            "type": "object",
            "required": ["id"],
            "properties": {
                "id": {"type": "number"}
            }
        },
        "name": {"type": "string"},
        "trigger": {
            "type": "object",
            "required": ["type"],
            "properties": {
                "type": {"type": "string"}
            }
        },
        "content": {
            "type": "object",
            "properties": {
                "emails": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                        "type": "object",
                        "required": ["fromEmail","subject"],
                        "properties": {
                            "fromEmail": {"type": "string", "format": "email"},
                            "subject": {"type": "string"}
                        }
                    }
                }
            }
        }
    }
}

I am trying to use jsonschema.Draft4Validator to validate a POSTed JSON object to check for validity, but I am having some issues trying to come up with better human readable messages from the returned errors.

Here is how I am validating:

from jsonschema import Draft4Validator

v = Draft4Validator(self.schema)
errors = sorted(v.iter_errors(autoresponder_workflow), key=lambda e: e.path)

if errors:
    print(', '.join(
        '%s %s %s' % (error.path.popleft(), error.path.pop(), error.message) for error in errors
    ))

The error message looks like:

content emails [] is too short, trigger type None is not of type u'string'

Im trying to create an error message that looks a little more like Please add at least one email to your workflow," "Please ensure that all of your emails contain subject lines,", etc

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189

4 Answers4

18

I had the need of displaying a more detailed custom message to the user when an error occurred. I did this by adding a field to the property in the schema and then looking it up on ValidationError.

import json
import jsonschema


def validate(_data, _schema):
    try:
        jsonschema.validate(_data, _schema)
    except jsonschema.ValidationError as e:
        return e.schema["error_msg"] if "error_msg" in e.schema else e.message 


schema = {
    "title": "index",
    "type": "object",
    "required": [
        "author",
        "description"
    ],
    "properties": {
        "author": {
            "type": "string",
            "description": "Name of the Author",
            "minLength": 3,
            "default": "",
            "error_msg": "Please provide a Author name"
        },
        "description": {
            "type": "string",
            "description": "Short description",
            "minLength": 10,
            "default": "",
            "error_msg": "Please add a short description"
        }
    }
}

data = {
    "author": "Jerakin",
    "description": ""
}
Jerakin
  • 455
  • 3
  • 17
  • This is really good. I was getting frustrated with the same issu and started thinking a field like "error_msg" would be a good addition to the jsonschema spec. This is a decent workaround. The only thing I would want to add is interpolation in the error message, maybe. However, considering that this is generally used for processing incoming data (user data), this could be inherently risky. Another option would be to make error_msg a callback. – rfportilla Apr 22 '20 at 15:46
  • fantastic solution. – Rolly Jul 26 '21 at 23:17
  • @Jerakin will this work when main keys - `author`, `description` are missing ? – user5319825 Jul 06 '22 at 07:58
  • 1
    @user5319825 You might have to add a `error_msg` in the root of the schema to catch that. But, as there could be a lot of errors at that stage I think it's better return the error message to the user. I have updated my answer with a tweak to `validate()`. – Jerakin Jul 13 '22 at 15:15
1

You could catch ValidationError exception and build a custom messages for the specific cases you want from ValidationError metadata. In this object you have:

info of the failed validator: its value and schema path info of the failed instance: its path and value at the time of the failed validation possible errors in subschemas cause (errors caused by a non-validation error)

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
0

I use this scrub function before running validate. It converts None to empty string as jsonschema does not like to handle None type. On validate it throws: jsonschema.exceptions.ValidationError: None is not of type u'string'.

def scrub(x):
    ret = copy.deepcopy(x)
    if isinstance(x, dict):
        for k, v in ret.items():
            ret[k] = scrub(v)
    if isinstance(x, (list, tuple)):
        for k, v in enumerate(ret):
            ret[k] = scrub(v)
    if x is None:
        ret = ''
    return ret
radtek
  • 34,210
  • 11
  • 144
  • 111
  • 5
    The JSON schema standard enforces this type checking, `jsonschema` just conforms to the standard. `None` (aka `"null"`) is in fact not of type `"string"`. If you want `null` to be a valid value, just set `"type": ["string", "null"]` for the object in your schema. Or use `"oneOf"` for more complicated types, and add `"null"` as one of the options. – CivFan Jul 05 '16 at 22:06
0

You could try jsonschema.exceptions.ValidationError:

try:
    v = Draft4Validator(self.schema)
except jsonschema.exceptions.ValidationError as err:
    print(err.message)
tuomastik
  • 4,559
  • 5
  • 36
  • 48
pjk
  • 547
  • 3
  • 14