#!/usr/bin/env python3 """JSON Schema Validator - validates JSON files against schema and outputs machine-readable report.""" import json import sys from pathlib import Path from typing import Any def validate_json_schema(data: dict, schema: dict) -> list[str]: """Validate a JSON dict against a schema. Returns list of errors.""" errors = [] if not isinstance(data, dict): return ["Root must be an object"] if "properties" not in schema: return [] # Check required fields for required in schema.get("required", []): if required not in data: errors.append(f"Missing required field: '{required}'") for prop, prop_schema in schema.get("properties", {}).items(): if prop in data: expected_type = prop_schema.get("type") if expected_type: type_map = {"string": str, "number": (int, float), "integer": int, "boolean": bool, "array": list, "object": dict} expected_py = type_map.get(expected_type) if expected_py and not isinstance(data[prop], expected_py): actual_type = type(data[prop]).__name__ errors.append(f"Property '{prop}' expected {expected_type}, got {actual_type}") return errors def main(): if len(sys.argv) < 3: print(json.dumps({"error": "Usage: json_validator.py "})) sys.exit(1) json_path = Path(sys.argv[1]) schema_path = Path(sys.argv[2]) if not json_path.exists(): print(json.dumps({"valid": False, "errors": [f"File not found: {json_path}"]})) sys.exit(1) if not schema_path.exists(): print(json.dumps({"valid": False, "errors": [f"Schema not found: {schema_path}"]})) sys.exit(1) try: with open(json_path) as f: data = json.load(f) except json.JSONDecodeError as e: print(json.dumps({"valid": False, "errors": [f"Invalid JSON: {e}"]})) sys.exit(1) try: with open(schema_path) as f: schema = json.load(f) except json.JSONDecodeError as e: print(json.dumps({"valid": False, "errors": [f"Invalid schema JSON: {e}"]})) sys.exit(1) errors = validate_json_schema(data, schema) result = { "valid": len(errors) == 0, "errors": errors, "file": str(json_path), "schema": str(schema_path) } print(json.dumps(result)) sys.exit(0 if result["valid"] else 1) if __name__ == "__main__": main()