JSON Schema

Validate JSON structure against a schema — API responses, config files, data pipelines.

Schema Structure

Minimal valid JSON Schema — defines an object with required fields
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "hostname": { "type": "string", "pattern": "^[a-z][a-z0-9-]+$" },
    "ip": { "type": "string", "format": "ipv4" },
    "vlan": { "type": "integer", "minimum": 1, "maximum": 4094 },
    "enabled": { "type": "boolean", "default": true }
  },
  "required": ["hostname", "ip", "vlan"],
  "additionalProperties": false
}
String with enum constraint — restrict to known values
{
  "role": {
    "type": "string",
    "enum": ["core", "distribution", "access", "management"]
  }
}
Array of objects — e.g., list of interfaces
{
  "interfaces": {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "vlan": { "type": "integer" }
      },
      "required": ["name"]
    },
    "minItems": 1
  }
}
Nested object — model hierarchical config
{
  "dns": {
    "type": "object",
    "properties": {
      "primary": { "type": "string", "format": "ipv4" },
      "secondary": { "type": "string", "format": "ipv4" },
      "search_domains": {
        "type": "array",
        "items": { "type": "string" }
      }
    },
    "required": ["primary"]
  }
}

Validation with ajv-cli

Validate a JSON file against a schema — exit code 0 means valid
ajv validate -s schema.json -d config.json
Validate multiple files against the same schema
ajv validate -s device-schema.json -d "devices/*.json"
Validate with verbose errors — shows which field failed and why
ajv validate -s schema.json -d config.json --errors=text 2>&1

Validation with Python jsonschema

Validate JSON from stdin using Python jsonschema — useful in pipelines
python3 -c "
import json, sys
from jsonschema import validate, ValidationError
schema = json.load(open('schema.json'))
data = json.load(sys.stdin)
try:
    validate(instance=data, schema=schema)
    print('Valid')
except ValidationError as e:
    print(f'Invalid: {e.message}', file=sys.stderr)
    sys.exit(1)
" < config.json
Validate all JSON files in a directory against a schema
for f in configs/*.json; do
  python3 -c "
import json, sys
from jsonschema import validate, ValidationError
schema = json.load(open('schema.json'))
data = json.load(open('$f'))
try:
    validate(instance=data, schema=schema)
    print(f'OK: $f')
except ValidationError as e:
    print(f'FAIL: $f — {e.message}', file=sys.stderr)
    sys.exit(1)
  "
done

Schema for API Responses

Schema for a paginated API response — validates structure before parsing
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "total": { "type": "integer", "minimum": 0 },
    "page": { "type": "integer", "minimum": 1 },
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "status": { "type": "string", "enum": ["active", "inactive", "pending"] }
        },
        "required": ["id", "name", "status"]
      }
    }
  },
  "required": ["total", "results"]
}

Schema for Config Files

Schema for an infrastructure inventory config — enforces structure on YAML/JSON configs
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "environment": { "type": "string", "enum": ["production", "staging", "lab"] },
    "devices": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/device"
      }
    }
  },
  "required": ["environment", "devices"],
  "$defs": {
    "device": {
      "type": "object",
      "properties": {
        "hostname": { "type": "string", "pattern": "^[a-z][a-z0-9.-]+$" },
        "mgmt_ip": { "type": "string", "format": "ipv4" },
        "role": { "type": "string", "enum": ["core", "distribution", "access"] },
        "vlans": {
          "type": "array",
          "items": { "type": "integer", "minimum": 1, "maximum": 4094 },
          "uniqueItems": true
        }
      },
      "required": ["hostname", "mgmt_ip", "role"]
    }
  }
}

Common Patterns

oneOf — value must match exactly one subschema (e.g., static vs DHCP)
{
  "addressing": {
    "oneOf": [
      {
        "type": "object",
        "properties": {
          "mode": { "const": "static" },
          "ip": { "type": "string", "format": "ipv4" },
          "gateway": { "type": "string", "format": "ipv4" }
        },
        "required": ["mode", "ip", "gateway"]
      },
      {
        "type": "object",
        "properties": {
          "mode": { "const": "dhcp" }
        },
        "required": ["mode"]
      }
    ]
  }
}
if/then — conditional validation based on a field value
{
  "if": {
    "properties": { "role": { "const": "core" } }
  },
  "then": {
    "required": ["bgp_asn", "loopback_ip"]
  }
}
Quick jq schema check — verify required keys exist before processing
jq -e '.hostname and .ip and .vlan' config.json > /dev/null 2>&1 \
  && echo "Valid" || echo "Missing required fields"