// JSON转换工具函数 /** * 将JSON转换为XML格式 */ export function jsonToXml(jsonString: string, options?: { rootName?: string }): string { try { const jsonObj = JSON.parse(jsonString); const rootName = options?.rootName || 'root'; // XML头部 let xml = '\n'; // 递归转换JSON对象为XML const jsonObjToXml = (obj: unknown, nodeName: string, indent = ''): string => { if (obj === null || obj === undefined) { return `${indent}<${nodeName}>\n`; } if (typeof obj !== 'object') { // 处理基本类型 return `${indent}<${nodeName}>${escapeXml(String(obj))}\n`; } if (Array.isArray(obj)) { // 处理数组:每个元素都使用相同的节点名 return obj.map(item => jsonObjToXml(item, nodeName, indent)).join(''); } // 处理对象 let result = `${indent}<${nodeName}>\n`; // 遍历对象属性 for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = (obj as Record)[key]; result += jsonObjToXml(value, key, indent + ' '); } } result += `${indent}\n`; return result; }; // 转换JSON对象并添加到XML字符串 xml += jsonObjToXml(jsonObj, rootName); return xml; } catch (error) { if (error instanceof Error) { throw new Error(`JSON转XML失败: ${error.message}`); } throw new Error('JSON转XML失败'); } } /** * 将XML转换为JSON格式 */ export function xmlToJson(xmlString: string): string { try { // 创建DOM解析器 const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlString, 'text/xml'); // 检查解析错误 const parseError = xmlDoc.getElementsByTagName('parsererror'); if (parseError.length > 0) { throw new Error('XML解析错误:无效的XML格式'); } // 递归处理XML节点 const processNode = (node: Element): unknown => { // 如果节点没有子元素,返回节点的文本内容 if (node.childNodes.length === 0 || (node.childNodes.length === 1 && node.childNodes[0].nodeType === 3)) { const text = node.textContent || ''; // 尝试转换为数字或布尔值 if (text === 'true') return true; if (text === 'false') return false; if (!isNaN(Number(text)) && text.trim() !== '') return Number(text); return text; } // 处理有子元素的节点 const result: Record = {}; const childElements = Array.from(node.children); // 对每个子元素类型进行分组,检测数组 const elementCounts: Record = {}; childElements.forEach(child => { if (!elementCounts[child.nodeName]) { elementCounts[child.nodeName] = []; } elementCounts[child.nodeName].push(child); }); // 处理每种子元素 for (const [nodeName, elements] of Object.entries(elementCounts)) { if (elements.length === 1) { // 单个元素 result[nodeName] = processNode(elements[0]); } else { // 多个同名元素,作为数组处理 result[nodeName] = elements.map(processNode); } } return result; }; // 处理XML文档的根元素 const root = xmlDoc.documentElement; const jsonObj = processNode(root); // 将对象转换为格式化的JSON字符串 return JSON.stringify(jsonObj, null, 2); } catch (error) { if (error instanceof Error) { throw new Error(`XML转JSON失败: ${error.message}`); } throw new Error('XML转JSON失败'); } } /** * 将JSON转换为CSV格式 */ export function jsonToCsv(jsonString: string, options?: { delimiter?: string; header?: boolean }): string { try { const jsonObj = JSON.parse(jsonString); const delimiter = options?.delimiter || ','; const includeHeader = options?.header !== false; // 处理数组数据 if (Array.isArray(jsonObj)) { if (jsonObj.length === 0) { return ''; } // 提取所有可能的字段 const fields = new Set(); jsonObj.forEach(item => { if (typeof item === 'object' && item !== null) { Object.keys(item).forEach(key => fields.add(key)); } }); const fieldNames = Array.from(fields); // 生成CSV头 let csv = includeHeader ? fieldNames.map(escapeCSV).join(delimiter) + '\n' : ''; // 生成CSV数据行 jsonObj.forEach(item => { const row = fieldNames.map(field => { const value = (item as Record)[field]; // 处理嵌套对象或数组 if (value !== null && typeof value === 'object') { return escapeCSV(JSON.stringify(value)); } return value !== undefined ? escapeCSV(String(value)) : ''; }); csv += row.join(delimiter) + '\n'; }); return csv; } else if (typeof jsonObj === 'object' && jsonObj !== null) { // 如果是单个对象,将其转换为单行CSV const fields = Object.keys(jsonObj); let csv = includeHeader ? fields.map(escapeCSV).join(delimiter) + '\n' : ''; // 生成数据行 const row = fields.map(field => { const value = (jsonObj as Record)[field]; // 处理嵌套对象或数组 if (value !== null && typeof value === 'object') { return escapeCSV(JSON.stringify(value)); } return value !== undefined ? escapeCSV(String(value)) : ''; }); csv += row.join(delimiter) + '\n'; return csv; } else { throw new Error('无法转换为CSV:JSON必须是对象或数组'); } } catch (error) { if (error instanceof Error) { throw new Error(`JSON转CSV失败: ${error.message}`); } throw new Error('JSON转CSV失败'); } } /** * 将CSV转换为JSON格式 */ export function csvToJson(csvString: string, options?: { delimiter?: string; header?: boolean }): string { try { const delimiter = options?.delimiter || ','; const hasHeader = options?.header !== false; // 分割CSV行 const lines = csvString.trim().split(/\r?\n/); if (lines.length === 0) { return '[]'; } // 解析CSV行,处理引号内的分隔符 const parseCSVLine = (line: string): string[] => { const result: string[] = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { // 引号处理:检查是否为转义的引号 if (i + 1 < line.length && line[i + 1] === '"') { current += '"'; i++; // 跳过下一个引号 } else { inQuotes = !inQuotes; } } else if (char === delimiter && !inQuotes) { // 找到分隔符且不在引号内 result.push(current); current = ''; } else { current += char; } } // 添加最后一个字段 result.push(current); return result; }; let headers: string[]; let startIndex: number; if (hasHeader) { // 使用第一行作为字段名 headers = parseCSVLine(lines[0]); startIndex = 1; } else { // 自动生成字段名 const firstLine = parseCSVLine(lines[0]); headers = firstLine.map((_, index) => `field${index}`); startIndex = 0; } // 处理CSV数据行 const result: Record[] = []; for (let i = startIndex; i < lines.length; i++) { if (!lines[i].trim()) continue; // 跳过空行 const values = parseCSVLine(lines[i]); const obj: Record = {}; // 将每个值与相应的字段名匹配 for (let j = 0; j < headers.length; j++) { if (j < values.length) { const value = values[j].trim(); // 尝试转换为合适的数据类型 if (value === 'true') obj[headers[j]] = true; else if (value === 'false') obj[headers[j]] = false; else if (!isNaN(Number(value)) && value !== '') obj[headers[j]] = Number(value); else obj[headers[j]] = value; } else { obj[headers[j]] = ''; } } result.push(obj); } return JSON.stringify(result, null, 2); } catch (error) { if (error instanceof Error) { throw new Error(`CSV转JSON失败: ${error.message}`); } throw new Error('CSV转JSON失败'); } } /** * 将JSON转换为YAML格式 */ export function jsonToYaml(jsonString: string): string { try { const jsonObj = JSON.parse(jsonString); // 递归转换JSON对象为YAML const convertToYaml = (obj: unknown, indent = 0): string => { if (obj === null || obj === undefined) { return 'null'; } const spaces = ' '.repeat(indent); if (typeof obj !== 'object') { // 处理基本类型 if (typeof obj === 'string') { // 检查是否需要引号 if ( /^[-:?!,[\]{}#&*!|>'"%@\`]|^[0-9]/.test(obj) || /^(true|false|null|y|n|yes|no|on|off)$/i.test(obj) || obj.includes('\n') || obj.includes(' ') ) { // 对特殊字符进行转义 return `'${obj.replace(/'/g, "''")}'`; } return obj; } return String(obj); } if (Array.isArray(obj)) { // 如果是空数组 if (obj.length === 0) { return '[]'; } // 处理数组:每个元素前加-号 return obj.map(item => { if (typeof item === 'object' && item !== null) { return `${spaces}- ${convertToYaml(item, indent + 2).trimStart()}`; } else { return `${spaces}- ${convertToYaml(item, indent)}`; } }).join('\n'); } // 如果是空对象 if (Object.keys(obj as Record).length === 0) { return '{}'; } // 处理对象 return Object.entries(obj as Record).map(([key, value]) => { const yamlKey = /^[a-zA-Z0-9_]+$/.test(key) ? key : `'${key}'`; if (typeof value === 'object' && value !== null) { return `${spaces}${yamlKey}:\n${convertToYaml(value, indent + 2)}`; } else { return `${spaces}${yamlKey}: ${convertToYaml(value, indent)}`; } }).join('\n'); }; return convertToYaml(jsonObj); } catch (error) { if (error instanceof Error) { throw new Error(`JSON转YAML失败: ${error.message}`); } throw new Error('JSON转YAML失败'); } } /** * 将YAML转换为JSON格式 */ export function yamlToJson(yamlString: string): string { try { // YAML解析是复杂的,通常使用库,但这里我们简化实现 // 主要解析简单的YAML格式:缩进表示层级,冒号分隔键值对 const lines = yamlString.split(/\r?\n/); // 处理缩进 const getIndent = (line: string): number => { let i = 0; while (i < line.length && line[i] === ' ') i++; return i; }; // 递归解析YAML const parseYaml = (currentIndex: number, minIndent: number): [unknown, number] => { let result: unknown = null; let i = currentIndex; // 确定当前行的缩进级别 const currentIndent = getIndent(lines[i]); let isArray = lines[i].trimStart().startsWith('-'); if (isArray) { // 解析数组 result = []; while (i < lines.length) { const line = lines[i]; if (!line.trim()) { i++; continue; // 跳过空行 } const lineIndent = getIndent(line); if (lineIndent < minIndent) break; // 缩进减少,退出当前层级 if (line.trimStart().startsWith('-')) { // 数组项 const itemText = line.trim().substring(1).trimStart(); if (itemText.includes(':')) { // 数组项是对象 const [key, valuePart] = splitKeyValue(itemText); const arrayItem: Record = {}; if (valuePart.trim()) { // 行内值 arrayItem[key] = parseScalar(valuePart.trim()); } else { // 子对象,下一行应该有更多缩进 if (i + 1 < lines.length && getIndent(lines[i + 1]) > lineIndent) { const [nestedObj, nextIndex] = parseYaml(i + 1, lineIndent + 2); arrayItem[key] = nestedObj; i = nextIndex - 1; // 回退一行,下次循环会递增 } else { arrayItem[key] = null; // 键值为空 } } (result as unknown[]).push(arrayItem); } else if (itemText) { // 简单值 (result as unknown[]).push(parseScalar(itemText)); } else { // 下一行有更多缩进 if (i + 1 < lines.length && getIndent(lines[i + 1]) > lineIndent) { const [nestedObj, nextIndex] = parseYaml(i + 1, lineIndent + 2); (result as unknown[]).push(nestedObj); i = nextIndex - 1; } else { (result as unknown[]).push(null); } } } else if (lineIndent === currentIndent) { // 同一缩进级别但不是数组项,说明数组结束 break; } i++; } } else { // 解析对象 result = {}; while (i < lines.length) { const line = lines[i]; if (!line.trim()) { i++; continue; // 跳过空行 } const lineIndent = getIndent(line); if (lineIndent < minIndent) break; // 缩进减少,退出当前层级 if (lineIndent === minIndent) { if (line.trimStart().startsWith('-')) { // 数组开始,与对象同级 isArray = true; break; } if (line.includes(':')) { // 键值对 const [key, valuePart] = splitKeyValue(line.trim()); if (valuePart.trim()) { // 行内值 (result as Record)[key] = parseScalar(valuePart.trim()); } else { // 子对象或数组,下一行应该有更多缩进 if (i + 1 < lines.length && getIndent(lines[i + 1]) > lineIndent) { const [nestedObj, nextIndex] = parseYaml(i + 1, lineIndent + 2); (result as Record)[key] = nestedObj; i = nextIndex - 1; // 回退一行,下次循环会递增 } else { (result as Record)[key] = null; // 键值为空 } } } } else if (lineIndent > minIndent) { // 缩进增加,属于上一个键的子内容 continue; } i++; } } return [result, i]; }; // 辅助函数:分割键值对 const splitKeyValue = (line: string): [string, string] => { const colonIndex = line.indexOf(':'); if (colonIndex === -1) return [line, '']; let key = line.substring(0, colonIndex).trim(); // 如果键名有引号,去掉引号 if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith('"') && key.endsWith('"'))) { key = key.substring(1, key.length - 1); } const value = line.substring(colonIndex + 1); return [key, value]; }; // 辅助函数:解析标量值 const parseScalar = (value: string): unknown => { // 去掉引号 if ((value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"'))) { return value.substring(1, value.length - 1); } // 解析为特定类型 if (value === 'null' || value === '~' || value === '') return null; if (value === 'true' || value === 'yes' || value === 'on') return true; if (value === 'false' || value === 'no' || value === 'off') return false; if (!isNaN(Number(value))) return Number(value); return value; }; // 开始解析 const [result] = parseYaml(0, 0); return JSON.stringify(result, null, 2); } catch (error) { if (error instanceof Error) { throw new Error(`YAML转JSON失败: ${error.message}`); } throw new Error('YAML转JSON失败'); } } // 辅助函数:转义XML特殊字符 function escapeXml(unsafe: string): string { return unsafe .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } // 辅助函数:转义CSV字段 function escapeCSV(value: string): string { // 如果字段包含逗号、双引号或换行符,需要用双引号包围并处理 if (value.includes(',') || value.includes('"') || value.includes('\n')) { // 双引号内的双引号需要再次转义 return `"${value.replace(/"/g, '""')}"`; } return value; }