UNPKG

@zhanghongping/json-sage-workflow-cli

Version:

An intelligent JSON processing workflow system with improved error handling and configuration

165 lines (136 loc) 4.95 kB
import Ajv from 'ajv'; import addFormats from 'ajv-formats'; import { JSONSchema7 } from 'json-schema'; interface ValidationOptions { strict?: boolean; coerceTypes?: boolean; removeAdditional?: boolean; useDefaults?: boolean; autoRepair?: boolean; } export class JsonValidator { private ajv: Ajv; private schemas: Map<string, any> = new Map(); private repairStrategies: Map<string, (value: any) => any> = new Map(); constructor(private options: ValidationOptions = {}) { this.ajv = new Ajv({ strict: options.strict ?? false, coerceTypes: options.coerceTypes ?? true, removeAdditional: options.removeAdditional ?? true, useDefaults: options.useDefaults ?? true, allErrors: true }); addFormats(this.ajv); this.initializeRepairStrategies(); } validate(data: any, schema: JSONSchema7): ValidationResult { const validate = this.ajv.compile(schema); const valid = validate(data); if (valid) { return { valid: true, data: data }; } const errors = validate.errors || []; let repairedData = data; if (this.options.autoRepair) { repairedData = this.repairData(data, errors, schema); } return { valid: false, errors: this.formatErrors(errors), repairedData: repairedData }; } private initializeRepairStrategies() { // 数字修复策略 this.repairStrategies.set('number', (value) => { if (typeof value === 'string') { const num = parseFloat(value); return isNaN(num) ? 0 : num; } return typeof value === 'number' ? value : 0; }); // 字符串修复策略 this.repairStrategies.set('string', (value) => { if (value === null || value === undefined) return ''; return String(value); }); // 布尔值修复策略 this.repairStrategies.set('boolean', (value) => { if (typeof value === 'string') { return value.toLowerCase() === 'true'; } return Boolean(value); }); // 数组修复策略 this.repairStrategies.set('array', (value) => { if (Array.isArray(value)) return value; return value ? [value] : []; }); // 对象修复策略 this.repairStrategies.set('object', (value) => { return typeof value === 'object' && value !== null ? value : {}; }); } private repairData(data: any, errors: any[], schema: JSONSchema7): any { let repairedData = { ...data }; for (const error of errors) { const path = error.instancePath.split('/').filter(Boolean); const type = this.getExpectedType(error, schema); if (type && this.repairStrategies.has(type)) { const repair = this.repairStrategies.get(type)!; this.updateValueAtPath(repairedData, path, repair); } } return repairedData; } private getExpectedType(error: any, schema: JSONSchema7): string | null { if (error.keyword === 'type') { return error.params.type; } // 从schema中查找期望的类型 const schemaPath = error.schemaPath.split('/').filter(Boolean); let currentSchema: any = schema; for (const part of schemaPath) { currentSchema = currentSchema[part]; if (!currentSchema) break; } return currentSchema?.type || null; } private updateValueAtPath(obj: any, path: string[], repair: (value: any) => any) { let current = obj; for (let i = 0; i < path.length - 1; i++) { const key = path[i]; if (!(key in current)) { current[key] = {}; } current = current[key]; } const lastKey = path[path.length - 1]; if (lastKey) { current[lastKey] = repair(current[lastKey]); } } private formatErrors(errors: any[]): ValidationError[] { return errors.map(error => ({ path: error.instancePath, message: error.message || 'Unknown error', keyword: error.keyword, params: error.params })); } } export interface ValidationResult { valid: boolean; errors?: ValidationError[]; repairedData?: any; data?: any; } export interface ValidationError { path: string; message: string; keyword: string; params: any; }