/** * JSON 导入导出(1.8.5)。 * * 导出行对象数组:[{"id":"1","name":"alice"}, …]。 * 导入要求为对象数组;缺列按空值补齐,并检查列集合一致性(不一致记为警告)。 * 解析失败返回错误而非抛异常。 */ export interface JsonParseResult { header: string[]; rows: string[][]; errors: string[]; warnings: string[]; } export class JsonCodec { static serialize(header: string[], rows: string[][]): string { const objects: Map[] = []; for (let i = 0; i < rows.length; i++) { const obj: Map = new Map(); for (let j = 0; j < header.length; j++) { const value: string | undefined = rows[i][j]; obj.set(header[j], value === undefined ? '' : value); } objects.push(obj); } const plain: Record[] = []; for (let i = 0; i < objects.length; i++) { const record: Record = {}; objects[i].forEach((value: string, key: string) => { record[key] = value; }); plain.push(record); } return JSON.stringify(plain, null, 2); } static parse(text: string): JsonParseResult { const errors: string[] = []; const warnings: string[] = []; let parsed: Object | null = null; try { parsed = JSON.parse(text) as Object; } catch (e) { const message: string = e instanceof Error ? e.message : String(e); return { header: [], rows: [], errors: ['invalid JSON: ' + message], warnings: [] }; } if (!Array.isArray(parsed)) { return { header: [], rows: [], errors: ['top-level JSON value must be an array'], warnings: [] }; } const array: Object[] = parsed as Object[]; if (array.length === 0) { return { header: [], rows: [], errors: [], warnings: ['empty array'] }; } const header: string[] = []; const first: Object = array[0]; if (first === null || Array.isArray(first) || typeof first !== 'object') { return { header: [], rows: [], errors: ['array items must be objects'], warnings: [] }; } const firstRecord: Record = first as Record; for (const key in firstRecord) { header.push(key); } const rows: string[][] = []; for (let i = 0; i < array.length; i++) { const item: Object = array[i]; if (item === null || Array.isArray(item) || typeof item !== 'object') { errors.push('item ' + i + ' is not an object'); continue; } const record: Record = item as Record; const row: string[] = []; for (let j = 0; j < header.length; j++) { const value: Object = record[header[j]]; if (value === undefined) { warnings.push('item ' + i + ' missing key ' + header[j]); row.push(''); } else { row.push(JsonCodec.stringifyValue(value)); } } for (const key in record) { if (header.indexOf(key) < 0) { warnings.push('item ' + i + ' has extra key ' + key + ' not in header'); } } rows.push(row); } return { header: header, rows: rows, errors: errors, warnings: warnings }; } private static stringifyValue(value: Object): string { if (value === null) { return ''; } if (typeof value === 'string') { return value as string; } if (typeof value === 'number' || typeof value === 'boolean') { return String(value); } return JSON.stringify(value); } }