Backend Development 8 min read

Advanced Usage of jsonschema for Complex JSON Validation in Python

This article demonstrates advanced jsonschema techniques in Python, covering complex data structures like arrays and nested objects, custom format and function validators, modular schema validation with RefResolver, and combining jsonschema with Voluptuous for data transformation.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Advanced Usage of jsonschema for Complex JSON Validation in Python

jsonschema is a powerful Python library for validating JSON data against defined schemas; this guide explores advanced usage including handling complex structures, creating custom validators, modularizing schemas with RefResolver, and integrating with other libraries for data transformation.

1. Complex Data Structures

1.1 Array validation – you can validate each element of an array against a schema.

import json
from jsonschema import validate, ValidationError
# Define JSON Schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "ages": {
            "type": "array",
            "items": {
                "type": "integer",
                "minimum": 0,
                "maximum": 120
            }
        }
    },
    "required": ["name", "ages"]
}
# JSON data to validate
data = {
    "name": "Alice",
    "ages": [25, 30, 35]
}
# Validation
try:
    validate(instance=data, schema=schema)
    print("JSON data is valid.")
except ValidationError as e:
    print(f"JSON data is invalid: {e}")

1.2 Nested object validation – you can validate nested objects against a schema.

# Define JSON Schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "address": {
            "type": "object",
            "properties": {
                "street": {"type": "string"},
                "city": {"type": "string"},
                "zipcode": {"type": "string"}
            },
            "required": ["street", "city"]
        }
    },
    "required": ["name", "address"]
}
# JSON data to validate
data = {
    "name": "Alice",
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "zipcode": "12345"
    }
}
# Validation
try:
    validate(instance=data, schema=schema)
    print("JSON data is valid.")
except ValidationError as e:
    print(f"JSON data is invalid: {e}")

2. Custom Validators

2.1 Custom format validator – for example, validating phone numbers.

from jsonschema import Draft7Validator, FormatChecker, ValidationError
# Custom format validator
def is_valid_phone_number(phone_number):
    return phone_number.isdigit() and len(phone_number) == 10
format_checker = FormatChecker()
format_checker.checks('phone')(is_valid_phone_number)
# Define JSON Schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "phone": {"type": "string", "format": "phone"}
    },
    "required": ["name", "phone"]
}
# JSON data to validate
data = {"name": "Alice", "phone": "1234567890"}
# Create validator and validate
validator = Draft7Validator(schema, format_checker=format_checker)
try:
    validator.validate(data)
    print("JSON data is valid.")
except ValidationError as e:
    print(f"JSON data is invalid: {e}")

2.2 Custom validation function – for example, checking that a number is even.

from jsonschema import Draft7Validator, ValidationError
# Custom validation function
def is_even(value):
    if value % 2 != 0:
        raise ValidationError(f"{value} is not an even number")
# Define JSON Schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "number": {"type": "integer"}
    },
    "required": ["name", "number"],
    "additionalProperties": False
}
# Create validator and add custom function
validator = Draft7Validator(schema)
validator.VALIDATORS["even"] = is_even
schema["properties"]["number"]["even"] = True
# JSON data to validate
data = {"name": "Alice", "number": 4}
# Validation
try:
    validator.validate(data)
    print("JSON data is valid.")
except ValidationError as e:
    print(f"JSON data is invalid: {e}")

3. Modular Validation with RefResolver

3.1 Schema files – person_schema.json and address_schema.json illustrate how schemas can be split into separate files and referenced.

{
    "$id": "http://example.com/schemas/person",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "address": {"$ref": "http://example.com/schemas/address"}
    },
    "required": ["name", "address"]
}

{
    "$id": "http://example.com/schemas/address",
    "type": "object",
    "properties": {
        "street": {"type": "string"},
        "city": {"type": "string"},
        "zipcode": {"type": "string"}
    },
    "required": ["street", "city"]
}

3.2 Validation using RefResolver – the resolver manages $ref links across files.

import json
from jsonschema import RefResolver, Draft7Validator, ValidationError
# Load schema files
with open('person_schema.json') as f:
    person_schema = json.load(f)
with open('address_schema.json') as f:
    address_schema = json.load(f)
# Create RefResolver
resolver = RefResolver(
    base_uri="http://example.com/schemas/",
    referrer=person_schema,
    store={
        "http://example.com/schemas/person": person_schema,
        "http://example.com/schemas/address": address_schema
    }
)
# Create validator
validator = Draft7Validator(person_schema, resolver=resolver)
# JSON data to validate
data = {
    "name": "Alice",
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "zipcode": "12345"
    }
}
# Validation
try:
    validator.validate(data)
    print("JSON data is valid.")
except ValidationError as e:
    print(f"JSON data is invalid: {e}")

4. Data Transformation with jsonschema and Voluptuous

Although jsonschema focuses on validation, it can be combined with libraries like Voluptuous to perform data conversion.

import json
import voluptuous as vol
from jsonschema import validate, ValidationError
# Define JSON Schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0},
        "email": {"type": "string", "format": "email"}
    },
    "required": ["name", "age"]
}
# Define Voluptuous Schema
vol_schema = vol.Schema({
    vol.Required('name'): str,
    vol.Required('age'): int,
    vol.Optional('email'): vol.Email()
})
# JSON data to validate
data = {"name": "Alice", "age": 30, "email": "[email protected]"}
# Validation and transformation
try:
    validate(instance=data, schema=schema)
    transformed_data = vol_schema(data)
    print("JSON data is valid and transformed:", transformed_data)
except (ValidationError, vol.Invalid) as e:
    print(f"JSON data is invalid: {e}")
Pythonjson validationjsonschemacustom validatorRefResolver
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.