UNPKG

@zhanghongping/json-sage-workflow-cli

Version:

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

150 lines 4.01 kB
const Ajv = require('ajv'); const addFormats = require('ajv-formats'); class JsonValidator { constructor(options = {}) { this.strict = options.strict || false; this.coerceTypes = options.coerceTypes || true; this.autoRepair = options.autoRepair || false; this.ajv = new Ajv({ strict: this.strict, coerceTypes: this.coerceTypes, useDefaults: true, removeAdditional: true }); addFormats(this.ajv); } validate(data, schema) { const validate = this.ajv.compile(schema); const valid = validate(data); if (valid) { return { valid: true, data }; } const result = { valid: false, errors: validate.errors }; if (this.autoRepair) { result.repairedData = this.repairData(data, schema, validate.errors); } return result; } repairData(data, schema, errors) { let repairedData = JSON.parse(JSON.stringify(data)); for (const error of errors) { const path = error.instancePath.split('/').slice(1); const value = this.getValueFromPath(repairedData, path); const repairedValue = this.repairValue(value, error, schema); if (path.length === 0) { repairedData = repairedValue; } else { this.setValueAtPath(repairedData, path, repairedValue); } } return repairedData; } repairValue(value, error, schema) { switch (error.keyword) { case 'type': return this.coerceType(value, error.params.type); case 'required': return this.addRequiredProperties(value, error, schema); case 'additionalProperties': return this.removeAdditionalProperties(value, schema); case 'format': return this.repairFormat(value, error.params.format); default: return value; } } coerceType(value, targetType) { switch (targetType) { case 'string': return String(value); case 'number': return Number(value); case 'boolean': return Boolean(value); case 'array': return Array.isArray(value) ? value : [value]; case 'object': return typeof value === 'object' ? value : {}; default: return value; } } addRequiredProperties(obj, error, schema) { const result = { ...obj }; const required = error.params.missingProperty; const propertySchema = schema.properties[required]; if (propertySchema) { result[required] = this.getDefaultValue(propertySchema.type); } return result; } removeAdditionalProperties(obj, schema) { const result = {}; const allowedProps = Object.keys(schema.properties || {}); for (const prop of allowedProps) { if (obj.hasOwnProperty(prop)) { result[prop] = obj[prop]; } } return result; } repairFormat(value, format) { // 处理常见的格式 switch (format) { case 'date-time': return new Date(value).toISOString(); case 'date': return new Date(value).toISOString().split('T')[0]; case 'email': return typeof value === 'string' ? value.toLowerCase() : ''; case 'uri': try { return new URL(value).toString(); } catch { return ''; } default: return value; } } getDefaultValue(type) { switch (type) { case 'string': return ''; case 'number': return 0; case 'boolean': return false; case 'array': return []; case 'object': return {}; default: return null; } } getValueFromPath(obj, path) { return path.reduce((current, key) => current && current[key], obj); } setValueAtPath(obj, path, value) { const lastKey = path.pop(); const target = path.reduce((current, key) => { if (!current[key]) { current[key] = {}; } return current[key]; }, obj); target[lastKey] = value; } } module.exports = { JsonValidator };