UNPKG

@zhanghongping/json-sage-workflow-cli

Version:

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

200 lines (169 loc) 6.11 kB
import { readFile } from 'fs/promises'; import { JSONSchema7 } from 'json-schema'; interface AnalyzerOptions { maxDepth?: number; sampleSize?: number; inferTypes?: boolean; } export class JsonAnalyzer { constructor(private options: AnalyzerOptions = { maxDepth: 10, sampleSize: 100, inferTypes: true }) {} async analyzeJsonStructure(filePath: string): Promise<JSONSchema7> { try { const content = await readFile(filePath, 'utf-8'); const data = JSON.parse(content); return this.generateSchema(data); } catch (error) { console.error(`Error analyzing JSON file ${filePath}:`, error); throw error; } } private generateSchema(data: any, depth: number = 0): JSONSchema7 { if (depth > (this.options.maxDepth || 10)) { return { type: 'any' }; } if (data === null) { return { type: 'null' }; } switch (typeof data) { case 'string': return this.analyzeString(data); case 'number': return this.analyzeNumber(data); case 'boolean': return { type: 'boolean' }; case 'object': if (Array.isArray(data)) { return this.analyzeArray(data, depth); } return this.analyzeObject(data, depth); default: return { type: 'any' }; } } private analyzeString(value: string): JSONSchema7 { // 检测日期格式 if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) { return { type: 'string', format: 'date-time' }; } // 检测邮箱格式 if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { return { type: 'string', format: 'email' }; } // 检测URL格式 if (/^https?:\/\//.test(value)) { return { type: 'string', format: 'uri' }; } return { type: 'string' }; } private analyzeNumber(value: number): JSONSchema7 { return { type: Number.isInteger(value) ? 'integer' : 'number' }; } private analyzeArray(arr: any[], depth: number): JSONSchema7 { if (arr.length === 0) { return { type: 'array', items: { type: 'any' } }; } // 分析数组元素类型 const sampleSize = Math.min(arr.length, this.options.sampleSize || 100); const samples = arr.slice(0, sampleSize); const itemSchemas = samples.map(item => this.generateSchema(item, depth + 1)); // 合并相似的模式 const mergedSchema = this.mergeSchemas(itemSchemas); return { type: 'array', items: mergedSchema }; } private analyzeObject(obj: Record<string, any>, depth: number): JSONSchema7 { const properties: Record<string, JSONSchema7> = {}; const required: string[] = []; for (const [key, value] of Object.entries(obj)) { properties[key] = this.generateSchema(value, depth + 1); if (value !== undefined && value !== null) { required.push(key); } } return { type: 'object', properties, required: required.length > 0 ? required : undefined }; } private mergeSchemas(schemas: JSONSchema7[]): JSONSchema7 { if (schemas.length === 0) return { type: 'any' }; if (schemas.length === 1) return schemas[0]; // 如果所有模式类型相同,合并它们 const types = new Set(schemas.map(s => s.type)); if (types.size === 1) { const type = schemas[0].type; if (type === 'object') { return this.mergeObjectSchemas(schemas as JSONSchema7[]); } if (type === 'array') { return this.mergeArraySchemas(schemas as JSONSchema7[]); } } // 如果类型不同,使用联合类型 return { anyOf: schemas }; } private mergeObjectSchemas(schemas: JSONSchema7[]): JSONSchema7 { const allProperties = new Set<string>(); schemas.forEach(schema => { if (schema.properties) { Object.keys(schema.properties).forEach(key => allProperties.add(key)); } }); const properties: Record<string, JSONSchema7> = {}; const required: string[] = []; allProperties.forEach(prop => { const propSchemas = schemas .filter(s => s.properties && s.properties[prop]) .map(s => s.properties![prop]); properties[prop] = this.mergeSchemas(propSchemas); // 如果所有模式都将此属性标记为必需 if (schemas.every(s => s.required && s.required.includes(prop))) { required.push(prop); } }); return { type: 'object', properties, required: required.length > 0 ? required : undefined }; } private mergeArraySchemas(schemas: JSONSchema7[]): JSONSchema7 { const itemSchemas = schemas .filter(s => s.items) .map(s => s.items as JSONSchema7); return { type: 'array', items: this.mergeSchemas(itemSchemas) }; } } export const analyzeJsonStructure = async ( filePath: string, options?: AnalyzerOptions ): Promise<JSONSchema7> => { const analyzer = new JsonAnalyzer(options); return analyzer.analyzeJsonStructure(filePath); };