jsonschema validate Python data: required fields and readable errors
Validate Python data against a JSON Schema, collect errors, and distinguish a bad instance from a bad schema.
Example check: Worked and guided code samples are checked against CPython 3.13. Practice uses Pyodide 0.29.5 (Python 3.13) in your browser. How we check lessons
Video lesson: jsonschema validate Python data: required fields and readable errors
The preview is stored on this site. YouTube loads only when you press Play. Watch on YouTube
Jump to a chapter
Read the video transcript
Select a timestamp to watch that moment on YouTube.
- 00:00
We receive JSON-like Python dictionaries from an API or a file. Every person record should have a nonblank name and an age of at least zero. We write a JSON Schema with properties for those fields and call jsonschema.validate. The surprising result is that a record without age may still pass. The age property describes how to validate age when it exists; it does not require age to exist. This lesson fixes that contract, checks the schema itself, and uses structured validation errors to tell us why data failed. It is about the installed jsonschema package, which performs runtime checks.
- 00:40
The browser exercise on the linked lesson page uses ordinary Python to rehearse the example rule; it is not a replacement for the package. I will first reproduce the missing-field problem, then compare it with a wrong type, add required and additionalProperties, and test several records. Watch how the validator reports an error without silently converting a string such as '20' into an integer. The code runs without an exception and prints accepted. That is correct behavior for this schema, but wrong for our intended application contract. The properties keyword only says which rules apply to keys that are present.
- 01:21
It does not demand their presence. A single successful test with a complete record could hide this mistake, so always test an absent required field. jsonschema.validate returns normally on success and raises ValidationError on invalid instance data; it does not return a Boolean flag that you need to inspect. A malformed schema is a separate issue and may raise SchemaError. Before processing many records, it is sensible to check the schema once. Also notice that JSON Schema object rules do not automatically ban extra fields. We need an explicit additionalProperties setting if the data contract allows only
- 02:01
name and age. Those choices define the actual acceptance boundary. We will make them visible in the next run. This table exposes the missing rules. The first record lacks age but passes because the schema has no required list. The second record has age as the string '20' and fails the integer type rule. The validator checks types; it does not coerce a string into a number. The third record has an extra role key and passes because additional properties are allowed by default. These are three independent dimensions: presence, value type, and unexpected keys.
- 02:41
We will add required for presence and set additionalProperties to false for the exact two-field contract. We will also give name a minimum length and a pattern that requires at least one non-space character, because minLength one alone would accept a single space. Age gets a minimum of zero. The browser task practices this same small contract with standard Python. Now the schema states the full contract. check_schema verifies that our schema document itself is valid for the selected draft. That is a configuration check, performed once before repeatedly validating records. The constructed validator can then check each instance. validate raises an
- 03:23
exception for an invalid record and returns normally here; is_valid gives a Boolean when we only need a quick decision. We chose a named draft because the behavior should not depend on whichever draft a library release happens to treat as its default. Additional properties are forbidden for this specific example; some APIs intentionally allow them, so change that flag according to your real contract. The pattern requires at least one non- space character in name; minLength one alone would not. The site exercise follows this same example policy using standard Python, while this local run demonstrates the jsonschema package API directly.
- 04:06
Each record violates a different rule, and iter_errors gives us ValidationError objects rather than forcing us to parse exception text. The validator property names the failed keyword. The path locates a present invalid value such as age, while it can be empty for a missing top-level key or an extra top- level property because the issue belongs to the object itself. Human-readable error.message is useful for display, but its exact wording is not a stable programmatic protocol. Use structured properties for logic and tests. If check_schema raises SchemaError, fix the schema configuration; if validation
- 04:46
raises ValidationError, inspect the input record. Do not silently convert '20' to 20 unless your API contract explicitly permits coercion before validation. The lesson page's browser task checks the same example fields with ordinary Python so you can practice the boundaries without installing jsonschema in the browser. Run the local code to see the package's full behavior, then adapt the schema to the real data shape your application accepts.
Understand the concept
The phrase jsonschema validate python refers to validating a decoded Python value against a JSON Schema document. Install the jsonschema package in the interpreter that runs your script with python -m pip install jsonschema. For one instance, jsonschema.validate(instance, schema) raises ValidationError if the data violates a valid schema. A schema can describe an object, required fields, types, minimum values, and whether extra properties are allowed. This is runtime validation, unlike a type annotation that only describes intended code shapes.
For repeated inputs, select a draft deliberately, for example Draft202012Validator. Call Draft202012Validator.check_schema(schema) once so a malformed schema fails clearly, construct the validator, and use validator.iter_errors(value) to collect all reported issues. Error.path identifies where an error occurred in the instance; it is empty for a missing top-level field because the absent property has no value path. Error.message explains the violation, but do not build application logic by parsing human-readable message text.
A common mistake is to put a field under properties but forget required. properties says what constraints apply when that field exists; it does not demand that the field exist. Another mistake is to assume a string containing digits is a JSON Schema integer. The Python package follows JSON Schema types, so the decoded string '20' fails an integer rule. For money, dates, and other formats, define a separate parsing or format policy rather than rely on implicit conversion.
The browser practice task uses only standard Python to rehearse the exact contract of this example: a person object with a nonblank name, an integer age of at least zero, and no extra keys. It is not a replacement implementation of JSON Schema. Run the separate local code on your computer for the real jsonschema API, then compare its behavior to the browser checks. The lesson demonstrates how to report a useful location for invalid data and when to check the schema itself.
- validate raises ValidationError for invalid data
- check_schema catches malformed schemas
- required differs from properties
- iter_errors collects issues
See it step by step
Read the code, predict the output, then compare it with the result.
01. Separate present and missing keys
value = {'name': 'Ada'}
print('name' in value, 'age' in value)True FalseThe missing age is a presence failure, separate from a wrong age type.
02. Check strict integer and minimum rules
for age in (20, '20', True, -1, 0):
print(repr(age), type(age) is int and age >= 0)20 True
'20' False
True False
-1 False
0 TrueThe browser exercise keeps the intended JSON Schema contract explicit without importing the external package.
03. Reject an extra property
allowed = {'name', 'age'}
record = {'name':'Ada', 'age':20, 'role':'admin'}
print(set(record) - allowed)
print(set(record) == allowed){'role'}
FalseadditionalProperties: false makes extra keys invalid in the local schema.
Run JSON Schema validation with the jsonschema package
Install jsonschema with python -m pip install jsonschema in your local environment. The browser task models the same rule with standard Python.
from jsonschema import Draft202012Validator
schema = {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'type': 'object',
'properties': {
'name': {'type': 'string', 'minLength': 1, 'pattern': r'\S'},
'age': {'type': 'integer', 'minimum': 0},
},
'required': ['name', 'age'],
'additionalProperties': False,
}
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
for value in ({'name': 'Ada', 'age': 20}, {'name': 'Ada', 'age': '20'}):
print(validator.is_valid(value))
for error in validator.iter_errors(value):
print(list(error.path), error.message)A closer look
Follow the reasoning, inspect each result, then try the suggested changes in the console below.
Write the schema's actual contract
A JSON Schema is data that describes acceptable data. For this example, the instance must be an object with name and age. Put each field's type under properties, put both names under required, set minimum on age, and use additionalProperties: false if extra keys are not allowed. The $schema URI identifies the draft used by the document. Keep the contract small enough to explain before introducing more keywords.
The browser code below checks that the expected keys are present, but this is only one piece of validation. The actual jsonschema package performs the full schema check in the local example above. Do not confuse a Python condition with a general JSON Schema implementation. A value may satisfy presence and still have the wrong type, an invalid range, or an unexpected extra field.
record = {'name': 'Ada', 'age': 20}
required = {'name', 'age'}
allowed = {'name', 'age'}
print(required <= set(record))
print(set(record) <= allowed)True
True- List required fields separately from properties.
- State whether additional keys are acceptable.
Validate with the installed package
Install jsonschema in the same environment that runs your script. When validating many records, call Draft202012Validator.check_schema(schema) once, then create the validator. is_valid gives a Boolean for a quick decision; validate raises ValidationError for one invalid value; iter_errors yields issues that a user interface can display. Use a named draft so future defaults do not quietly change how your schema is interpreted.
One common failure is to assume a missing field is automatically invalid because it appears under properties. It is not: required must list it. Another is to expect a numeric-looking string to turn into an integer. Validation reports that difference; it does not coerce. The browser exercise explicitly checks a strict Python integer, including rejecting bool as age, to illustrate this contract without relying on the external package.
for age in (20, '20', True, -1, 0):
print(repr(age), type(age) is int and age >= 0)20 True
'20' False
True False
-1 False
0 True- Run the local package example after installing jsonschema.
- Compare a valid age with string and Boolean values.
Report errors without guessing from messages
A ValidationError contains structured context such as path and validator, as well as a human-readable message. For a nested invalid value, path points to its location. For an absent top-level field, the path can be empty because there is no invalid value at the missing key. Display the message for a person, but use structured properties for program logic. The exact wording of a message is not a stable machine-readable interface.
When a schema itself is invalid, check_schema raises SchemaError. That is a configuration problem, not a bad user record. Separate these cases in logs and tests. If a form accepts only a subset of the schema's possibilities, validate that form's rules too; do not silently change incoming data to make it pass. The complete local snippet demonstrates is_valid and iter_errors, while this browser exercise focuses on the example contract.
records = [{'name':'Ada'}, {'name':'Ada','age':'20'}, {'name':'Ada','age':20}]
for record in records:
print('age' in record, type(record.get('age')) is int)False False
True False
True True- Distinguish a missing field from a present field with the wrong type.
- Use structured error paths from jsonschema in the local application.
Try it in Python
Edit the example and run it. Python starts in your browser the first time you click Run.
Python console
Ready to runNeed input()? Add one value per line
Your output appears here.
Need a hint?
Check type(value) is dict, set(value) == {'name', 'age'}, isinstance(name, str) and name.strip(), and type(age) is int and age >= 0.
Complete the task and select Check task to verify your code.
Quick quiz
Three questions. You can change your answers and try again.
Typical mistakes
Everyone meets these errors. See what causes them and how to fix them.
Forgetting required
{'properties': {'age': {'type': 'integer'}}}Add 'required': ['age'] when age must be present.What happens: An object without age may still validate.
properties and required have separate roles in JSON Schema.
Treating validation as coercion
validator.validate({'age': '20'}) # expects 20Parse or reject the string before validation according to your input contract.What happens: The string fails the integer rule.
jsonschema checks the instance; it does not transform a value into another type.