Claude Code for jsonschema: JSON Schema Validation in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for jsonschema: JSON Schema Validation in Python
AI

Claude Code for jsonschema: JSON Schema Validation in Python

Published: January 7, 2028
Read time: 5 min read
By: Claude Skills 360

jsonschema validates Python dicts against JSON Schema. pip install jsonschema. Validate: from jsonschema import validate. validate(instance={"name":"Alice"}, schema={"type":"object","properties":{"name":{"type":"string"}}}). Invalid: raises ValidationError. Schema types: "string", "integer", "number", "boolean", "array", "object", "null". Multiple types: {"type":["string","null"]}. Required: {"required":["name","email"]}. Properties: {"properties":{"age":{"type":"integer","minimum":0}}}. Additional: {"additionalProperties":False} — reject unknown keys. {"additionalProperties":{"type":"string"}}. Pattern properties: {"patternProperties":{"^[A-Z]+$":{"type":"integer"}}}. String constraints: {"minLength":1,"maxLength":100,"pattern":"^\\d{5}$"}. Number: {"minimum":0,"maximum":100,"exclusiveMinimum":0,"multipleOf":5}. Array: {"type":"array","items":{"type":"string"},"minItems":1,"maxItems":10,"uniqueItems":True}. Enum: {"enum":["user","admin","mod"]}. Const: {"const":"active"}. Format: {"format":"email"} — requires jsonschema[format-nongpl]. "uri", "date-time", "date", "ipv4", "uuid". validate(data, schema, format_checker=FormatChecker()). anyOf: {"anyOf":[{"type":"string"},{"type":"integer"}]}. oneOf: exactly one. allOf: all must match. not: {"not":{"type":"null"}}. if-then-else: {"if":{"properties":{"role":{"const":"admin"}}},"then":{"required":["admin_key"]}}. $ref: {"$ref":"#/$defs/Address"}. $defs / definitions. Draft validator: from jsonschema import Draft7Validator, Draft202012Validator. v = Draft7Validator(schema). list(v.iter_errors(data)) — collect all errors. error.message, error.path, error.schema_path. Draft7Validator.check_schema(schema) — validate the schema itself. jsonschema.exceptions.best_match(errors) — pick most relevant error. RefResolver for external $ref URIs. Claude Code generates JSON Schema definitions, OpenAPI component schemas, and validation pipelines.

CLAUDE.md for jsonschema

## jsonschema Stack
- Version: jsonschema >= 4.21 | pip install "jsonschema[format-nongpl]"
- Draft: Draft7Validator(schema) | Draft202012Validator for latest features
- Validate: validate(data, schema, format_checker=FormatChecker()) — raises on first
- Bulk: list(validator.iter_errors(data)) — collects all ValidationErrors
- Error: exc.message | exc.path (deque of keys) | exc.schema_path
- Reuse: $defs + $ref — inline schema composition without duplication
- Format: {"format":"email"} — requires format_checker=FormatChecker() at call

jsonschema Validation Pipeline

# app/json_schemas.py — JSON Schema definitions and validation utilities
from __future__ import annotations

from collections import deque
from typing import Any, Iterator

from jsonschema import Draft7Validator, FormatChecker, ValidationError, validate
from jsonschema.exceptions import best_match


# ─────────────────────────────────────────────────────────────────────────────
# Shared schema fragments — referenced via $ref
# ─────────────────────────────────────────────────────────────────────────────

_DEFS = {
    "Address": {
        "type": "object",
        "required": ["street", "city", "state", "postal_code"],
        "additionalProperties": False,
        "properties": {
            "street":      {"type": "string", "minLength": 1, "maxLength": 200},
            "city":        {"type": "string", "minLength": 1, "maxLength": 100},
            "state":       {"type": "string", "pattern": r"^[A-Z]{2}$"},
            "postal_code": {"type": "string", "pattern": r"^\d{5}(-\d{4})?$"},
            "country":     {"type": "string", "minLength": 2, "maxLength": 2, "default": "US"},
        },
    },
    "Money": {
        "type": "object",
        "required": ["amount", "currency"],
        "additionalProperties": False,
        "properties": {
            "amount":   {"type": "number", "exclusiveMinimum": 0},
            "currency": {"type": "string", "minLength": 3, "maxLength": 3,
                         "pattern": r"^[A-Z]{3}$"},
        },
    },
    "Pagination": {
        "type": "object",
        "properties": {
            "page":      {"type": "integer", "minimum": 1, "default": 1},
            "page_size": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
        },
    },
}


# ─────────────────────────────────────────────────────────────────────────────
# User schemas
# ─────────────────────────────────────────────────────────────────────────────

CREATE_USER_SCHEMA: dict = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": _DEFS,
    "type": "object",
    "required": ["email", "first_name", "last_name", "password"],
    "additionalProperties": False,
    "properties": {
        "email": {
            "type":      "string",
            "format":    "email",   # requires FormatChecker
            "maxLength": 254,
        },
        "first_name": {"type": "string", "minLength": 1, "maxLength": 100},
        "last_name":  {"type": "string", "minLength": 1, "maxLength": 100},
        "password": {
            "type":      "string",
            "minLength": 8,
            "maxLength": 72,
        },
        "role": {
            "type":    "string",
            "enum":    ["user", "moderator", "admin"],
            "default": "user",
        },
        "age": {
            "type":    ["integer", "null"],
            "minimum": 0,
            "maximum": 130,
        },
        "address": {
            "oneOf": [
                {"$ref": "#/$defs/Address"},
                {"type": "null"},
            ],
        },
        "metadata": {
            "type": "object",
            "additionalProperties": {"type": "string"},
        },
    },
    # Conditional — if admin, phone is required
    "if":   {"properties": {"role": {"const": "admin"}}},
    "then": {"required": ["email"]},   # simplified: real rule would require phone
}

UPDATE_USER_SCHEMA: dict = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "additionalProperties": False,
    "minProperties": 1,   # at least one field must be present
    "properties": {
        "first_name": {"type": "string", "minLength": 1, "maxLength": 100},
        "last_name":  {"type": "string", "minLength": 1, "maxLength": 100},
        "role":       {"type": "string", "enum": ["user", "moderator", "admin"]},
    },
}


# ─────────────────────────────────────────────────────────────────────────────
# Product schema
# ─────────────────────────────────────────────────────────────────────────────

PRODUCT_SCHEMA: dict = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": _DEFS,
    "type": "object",
    "required": ["sku", "name", "price", "stock", "category"],
    "additionalProperties": False,
    "properties": {
        "sku":  {
            "type":    "string",
            "pattern": r"^[A-Z0-9]+-\d{4,}$",
        },
        "name":  {"type": "string", "minLength": 1, "maxLength": 200},
        "price": {
            "type":             "number",
            "exclusiveMinimum": 0,
            "maximum":          100_000,
        },
        "stock": {"type": "integer", "minimum": 0},
        "category": {
            "type": "string",
            "enum": ["Electronics", "Clothing", "Books", "Home", "Sports"],
        },
        "is_active": {"type": "boolean", "default": True},
        "tags": {
            "type":        "array",
            "items":       {"type": "string", "minLength": 1},
            "uniqueItems": True,
            "default":     [],
        },
        "weight_kg": {"type": ["number", "null"], "minimum": 0},
        "price_obj": {"$ref": "#/$defs/Money"},
    },
}


# ─────────────────────────────────────────────────────────────────────────────
# Order schema
# ─────────────────────────────────────────────────────────────────────────────

ORDER_LINE_SCHEMA: dict = {
    "type": "object",
    "required":            ["product_id", "sku", "quantity", "unit_price"],
    "additionalProperties": False,
    "properties": {
        "product_id": {"type": "string", "minLength": 1},
        "sku":        {"type": "string", "pattern": r"^[A-Z0-9]+-\d{4,}$"},
        "quantity":   {"type": "integer", "minimum": 1, "maximum": 10_000},
        "unit_price": {"type": "number",  "exclusiveMinimum": 0},
    },
}

CREATE_ORDER_SCHEMA: dict = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {**_DEFS, "OrderLine": ORDER_LINE_SCHEMA},
    "type": "object",
    "required": ["user_id", "lines"],
    "additionalProperties": False,
    "properties": {
        "user_id": {"type": "string", "minLength": 1},
        "lines": {
            "type":     "array",
            "items":    {"$ref": "#/$defs/OrderLine"},
            "minItems": 1,
        },
        "notes": {
            "type":      ["string", "null"],
            "maxLength": 500,
        },
        "shipping_address": {
            "oneOf": [{"$ref": "#/$defs/Address"}, {"type": "null"}],
        },
    },
}


# ─────────────────────────────────────────────────────────────────────────────
# Validation utilities
# ─────────────────────────────────────────────────────────────────────────────

_FORMAT_CHECKER = FormatChecker()


def iter_errors(data: Any, schema: dict) -> Iterator[ValidationError]:
    """Yield all validation errors — does not stop at first failure."""
    validator = Draft7Validator(schema, format_checker=_FORMAT_CHECKER)
    yield from validator.iter_errors(data)


def collect_errors(data: Any, schema: dict) -> list[dict]:
    """Return all validation errors as serializable dicts."""
    errors = []
    for error in iter_errors(data, schema):
        path = ".".join(str(p) for p in error.absolute_path) or "(root)"
        errors.append({
            "path":    path,
            "message": error.message,
            "value":   repr(error.instance)[:120],
        })
    return errors


def validate_or_raise(data: Any, schema: dict) -> None:
    """Raise ValidationError with the most relevant error on failure."""
    validator = Draft7Validator(schema, format_checker=_FORMAT_CHECKER)
    errors = list(validator.iter_errors(data))
    if errors:
        raise best_match(errors)


def is_valid(data: Any, schema: dict) -> bool:
    """Return True/False without raising."""
    return Draft7Validator(schema, format_checker=_FORMAT_CHECKER).is_valid(data)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    # Valid user
    new_user = {
        "email":      "[email protected]",
        "first_name": "Alice",
        "last_name":  "Smith",
        "password":   "SecurePass1!",
        "role":       "admin",
        "address": {
            "street":      "123 Main St",
            "city":        "Springfield",
            "state":       "IL",
            "postal_code": "62701",
        },
    }
    errors = collect_errors(new_user, CREATE_USER_SCHEMA)
    print(f"User errors: {errors}")  # []

    # Invalid user — multiple problems
    bad_user = {
        "email":      "not-an-email",     # format fail
        "first_name": "",                  # minLength fail
        "password":   "short",             # minLength fail
        "role":       "superuser",         # enum fail
        "extra_key":  "oops",             # additionalProperties fail
    }
    errors = collect_errors(bad_user, CREATE_USER_SCHEMA)
    print(f"\n{len(errors)} validation errors:")
    for e in errors:
        print(f"  [{e['path']}] {e['message']}")

    # Product
    product = {
        "sku":      "ELEC-1001",
        "name":     "Mechanical Keyboard",
        "price":    89.99,
        "stock":    50,
        "category": "Electronics",
        "tags":     ["keyboard", "mechanical"],
    }
    print(f"\nProduct valid: {is_valid(product, PRODUCT_SCHEMA)}")

    # Order with nested lines
    order = {
        "user_id": "usr-001",
        "lines": [
            {"product_id": "p1", "sku": "PROD-1001", "quantity": 2, "unit_price": 19.99},
            {"product_id": "p2", "sku": "BOOK-2005", "quantity": 1, "unit_price": 39.99},
        ],
    }
    errors = collect_errors(order, CREATE_ORDER_SCHEMA)
    print(f"Order errors: {errors}")  # []

    # best_match — single most relevant error
    try:
        validate_or_raise(bad_user, CREATE_USER_SCHEMA)
    except ValidationError as exc:
        print(f"\nbest_match error: {exc.message}")
        print(f"  path: {list(exc.absolute_path) or '(root)'}")

    # Schema self-check
    Draft7Validator.check_schema(CREATE_USER_SCHEMA)
    print("\nSchema self-check passed.")

For the Cerberus alternative — Cerberus uses its own schema dict syntax ({"type":"string","minlength":1}) that is Python-specific and not portable to other languages, while jsonschema validates against the standard JSON Schema specification (Draft-07, Draft 2020-12) whose schemas can be embedded directly in OpenAPI specs and reused by JavaScript, Go, or Rust validators — one schema source of truth across the stack. For the Pydantic alternative — BaseModel validates at the Python object layer and cannot validate arbitrary dicts produced by json.loads() without first constructing the model, while Draft7Validator(schema).iter_errors(raw_dict) validates the raw parsed JSON in one call with no class definitions needed, making it the right choice for validating OpenAPI request/response payloads against the spec’s own schema objects or for building a generic validation middleware that selects the schema at runtime. The Claude Skills 360 bundle includes jsonschema skill sets covering Draft7Validator and Draft202012Validator, properties/required/additionalProperties, type/enum/const constraints, string pattern/format/minLength/maxLength, number minimum/maximum/exclusiveMinimum/multipleOf, array items/minItems/uniqueItems, anyOf/oneOf/allOf/not composition, if-then-else conditional validation, $defs and $ref for schema reuse, iter_errors for bulk collection, and best_match for user-friendly error reporting. Start with the free tier to try JSON Schema validation code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free