JSON Schema Validation in Python: A Practical Guide
Installation
First, install the required library:
pip install jsonschema
Validating Basic Data Types
The fololwing example demonstrates validating an object with string and numeric fields:
from jsonschema import validate
definition = {
"type": "object",
"properties": {
"product_name": {"type": "string"},
"unit_price": {"type": "number"},
"quantity": {"type": "integer"}
}
}
# Valid input - passes validation
validate(
instance={"product_name": "Wireless Mouse", "unit_price": 29.99, "quantity": 5},
schema=definition
)
# Invalid input - string provided instead of number
validate(
instance={"product_name": "Keyboard", "unit_price": "fifty"},
schema=definition
)
Validating Arrays with Speciifc Element Types
This example shows how to validate an array where each element must be of a sepcific type:
from jsonschema import validate
definition = {
"type": "object",
"properties": {
"categories": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
# Valid: array of strings
validate(
instance={"categories": ["electronics", "furniture", "clothing"]},
schema=definition
)
# Invalid: array contains integers instead of strings
validate(
instance={"categories": [1, 2, 3]},
schema=definition
)
Enum Validation
Use the enum keyword to restrict a field to specific allowed values:
from jsonschema import validate
definition = {
"type": "object",
"properties": {
"status": {"enum": ["active", "inactive", "pending"]}
}
}
# Valid: status is one of the allowed values
validate(
instance={"status": "active"},
schema=definition
)
# Invalid: status value not in the enum
validate(
instance={"status": "unknown"},
schema=definition
)
Requiring Specific Fields
The required keyword ensures certain fields must be present in the object:
from jsonschema import validate
definition = {
"type": "object",
"properties": {
"username": {"type": "string", "minLength": 3, "maxLength": 50},
"email": {"type": "string", "format": "email"},
"role": {"enum": ["admin", "user", "guest"]}
},
"required": ["username", "email"]
}
# Valid: both required fields present
validate(
instance={"username": "john_doe", "email": "john@example.com"},
schema=definition
)
# Invalid: missing required 'email' field
validate(
instance={"username": "jane_doe"},
schema=definition
)
Validating Nested Structures
JSON Schema supports complex nested objects with their own validation rules:
from jsonschema import validate
definition = {
"type": "object",
"properties": {
"order_id": {"type": "integer", "minimum": 1},
"customer": {
"type": "object",
"properties": {
"full_name": {"type": "string"},
"address": {"type": "string"},
"phone": {"type": "string", "pattern": "^[0-9]{10}$"}
},
"required": ["full_name", "address", "phone"]
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_id": {"type": "integer"},
"quantity": {"type": "integer", "minimum": 1}
},
"required": ["product_id", "quantity"]
}
}
},
"required": ["order_id", "customer", "items"]
}
validate(
instance={
"order_id": 1001,
"customer": {
"full_name": "Alice Smith",
"address": "123 Main Street",
"phone": "1234567890"
},
"items": [
{"product_id": 101, "quantity": 2},
{"product_id": 102, "quantity": 1}
]
},
schema=definition
)
Integration with Django REST Framework
Here's a practical example of using JSON Schema validation in a Django API endpoint:
from jsonschema import validate
from jsonschema import ValidationError
from functools import wraps
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
class RequestValidationException(APIException):
status_code = 400
default_detail = "Request validation failed"
default_code = "validation_error"
def validate_request(schema):
def decorator(view_func):
@wraps(view_func)
def wrapper(self, request, *args, **kwargs):
try:
validate(instance=request.data, schema=schema)
except ValidationError as error:
raise RequestValidationException(
detail={"error": error.message, "field": ".".join(str(p) for p in error.path)}
)
return view_func(self, request, *args, **kwargs)
return wrapper
return decorator
class UserRegistrationView(APIView):
@validate_request({
"type": "object",
"properties": {
"username": {"type": "string", "minLength": 3, "maxLength": 30},
"password": {"type": "string", "minLength": 8},
"profile": {
"type": "object",
"properties": {
"full_name": {"type": "string"},
"department": {"type": "string"},
"employee_id": {"type": "string", "minLength": 5}
},
"required": ["full_name", "department", "employee_id"]
}
},
"required": ["username", "password", "profile"]
})
def post(self, request):
# Process valid request here
return Response({"message": "User registered successfully"})
Sample valid request payload:
{
"username": "admin_user",
"password": "securepass123",
"profile": {
"full_name": "Robert Johnson",
"department": "Engineering",
"employee_id": "EMP001"
}
}
Additional Resources
For more advanced JSON Schema features and specifications, refer to the official documentation: