@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
text/typescript
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;
}