code icon Code

Validate JSON Schema

Validate JSON data against JSON Schema

Source Code

import fs from "fs";
import path from "path";
import Ajv from "ajv";

const [inputPath, schemaPath, outputPath] = process.argv.slice(2);

if (!inputPath || !schemaPath || !outputPath) {
  console.error("Usage: inputPath schemaPath outputPath");
  process.exit(1);
}

try {
  console.log(`Validating JSON against schema...`);
  console.log(`  Data: ${inputPath}`);
  console.log(`  Schema: ${schemaPath}`);

  const data = JSON.parse(fs.readFileSync(inputPath, "utf-8"));
  const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));

  const ajv = new Ajv({ allErrors: true, verbose: true });
  const validate = ajv.compile(schema);

  // Handle both single object and array validation
  const items = Array.isArray(data) ? data : [data];
  const isArray = Array.isArray(data);

  const valid = [];
  const invalid = [];
  const errors = [];

  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    const isValid = validate(item);

    if (isValid) {
      valid.push(isArray ? { index: i, item } : item);
    } else {
      const itemErrors = validate.errors.map((err) => ({
        path: err.instancePath || "/",
        keyword: err.keyword,
        message: err.message,
        params: err.params,
      }));

      invalid.push(isArray ? { index: i, item, errors: itemErrors } : { item, errors: itemErrors });

      errors.push(
        ...itemErrors.map((e) => ({
          index: isArray ? i : null,
          ...e,
        }))
      );
    }
  }

  const result = {
    valid: isArray ? valid : valid.length > 0,
    validCount: valid.length,
    invalidCount: invalid.length,
    totalCount: items.length,
    validItems: isArray ? valid : undefined,
    invalidItems: invalid.length > 0 ? invalid : undefined,
    errors: errors.length > 0 ? errors : undefined,
  };

  // Ensure output directory exists
  const dir = path.dirname(outputPath);
  if (dir && dir !== ".") {
    fs.mkdirSync(dir, { recursive: true });
  }

  fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));

  const allValid = invalid.length === 0;

  console.log(`\n${allValid ? "✓" : "✗"} Validation ${allValid ? "passed" : "failed"}`);
  console.log(`  Total items: ${items.length}`);
  console.log(`  Valid: ${valid.length}`);
  console.log(`  Invalid: ${invalid.length}`);

  if (errors.length > 0) {
    console.log(`  Errors:`);
    for (const err of errors.slice(0, 5)) {
      const prefix = err.index !== null ? `[${err.index}]` : "";
      console.log(`    ${prefix}${err.path}: ${err.message}`);
    }
    if (errors.length > 5) {
      console.log(`    ... and ${errors.length - 5} more`);
    }
  }

  console.log(`  Written to: ${outputPath}`);

  console.log(
    JSON.stringify({
      success: true,
      inputPath,
      schemaPath,
      outputPath,
      allValid,
      validCount: valid.length,
      invalidCount: invalid.length,
      errorCount: errors.length,
    })
  );
} catch (error) {
  console.error("Error:", error.message);
  process.exit(1);
}