如何提取XML中step与compref节点并实现XML与数组互转?
解决XML与指定格式数组的双向转换问题
一、XML转目标数组(包含step和compref节点提取)
要同时提取step和compref节点并按深度优先顺序生成数组,需要递归遍历<steps>下的所有节点,先处理compref本身再递归其内部子节点,同时维护序号计数。
实现代码
// 将XML字符串解析为DOM对象 function parseXML(xmlStr) { const parser = new DOMParser(); return parser.parseFromString(xmlStr, 'application/xml'); } // 深度优先遍历生成目标数组 function xmlToStepsArray(xmlDoc) { const result = []; let stepCounter = 1; // 递归处理单个节点 function traverse(node) { if (node.nodeType !== Node.ELEMENT_NODE) return; if (node.tagName === 'compref') { // 记录compref节点信息 result.push({ step: stepCounter++, compref: true, result: `ref-${node.getAttribute('ref')}`, id: node.getAttribute('id') // 保留原id,方便反向解析 }); // 递归处理compref内部子节点 Array.from(node.children).forEach(child => traverse(child)); } else if (node.tagName === 'step') { // 提取step节点的参数文本 const paramStrings = Array.from(node.querySelectorAll('parameterizedString')); result.push({ step: stepCounter++, action: paramStrings[0]?.textContent || '', result: paramStrings[1]?.textContent || '', type: node.getAttribute('type'), // 保留原节点类型 id: node.getAttribute('id') // 保留原id }); } } // 从根steps节点开始遍历 const stepsRoot = xmlDoc.querySelector('steps'); Array.from(stepsRoot.children).forEach(child => traverse(child)); return result; } // 使用示例 const xmlStr = `<steps id="0" last="4"> <step id="2" type="ValidateStep"> <parameterizedString isformatted="true">Stp 001</parameterizedString> <parameterizedString isformatted="true">Act 001</parameterizedString> </step> <compref id="4" ref="89"> <step id="3" type="ValidateStep"> <parameterizedString isformatted="true">Stp 003</parameterizedString> <parameterizedString isformatted="true">Act 003</parameterizedString> </step> </compref> </steps>`; const xmlDoc = parseXML(xmlStr); const stepsArray = xmlToStepsArray(xmlDoc); console.log(stepsArray);
代码说明
parseXML:将XML字符串转换为可操作的DOM对象;traverse递归函数:优先处理compref节点,记录其ref值后再遍历内部子节点;遇到step则提取两个parameterizedString的文本;- 保留
step的type、id和compref的id字段,为反向解析精准还原XML做准备。
二、数组转回原XML格式
反向转换需要将线性数组恢复为嵌套的XML结构,先把数组转为树形结构,再递归生成XML字符串。
实现代码
// 将线性数组转为嵌套树形结构 function arrayToTree(stepsArray) { // 递归构建树形结构,返回子节点数组和当前处理到的索引 function buildTree(index) { const nodes = []; while (index < stepsArray.length) { const item = stepsArray[index]; if ('compref' in item) { const comprefNode = { type: 'compref', ref: item.result.replace('ref-', ''), id: item.id || Math.floor(Math.random() * 100), children: [] }; index++; // 递归处理compref的子节点 const { children, newIndex } = buildTree(index); comprefNode.children = children; nodes.push(comprefNode); index = newIndex; } else { const stepNode = { type: 'step', action: item.action, result: item.result, typeAttr: item.type || 'ValidateStep', id: item.id || Math.floor(Math.random() * 100) }; nodes.push(stepNode); index++; } } return { children: nodes, newIndex: index }; } const { children } = buildTree(0); // 生成根steps节点,last值取最大节点id const maxId = Math.max(...children.flatMap(node => { if (node.type === 'compref') { return [node.id, ...node.children.flatMap(c => c.id)]; } else { return [node.id]; } }), 0); return { type: 'steps', id: '0', last: maxId.toString(), children }; } // 将树形结构转为格式化的XML字符串 function treeToXML(tree) { function generateNode(node, indentLevel = 0) { const indent = ' '.repeat(indentLevel); if (node.type === 'steps') { const childrenXml = node.children.map(child => generateNode(child, indentLevel + 1)).join('\n'); return `${indent}<steps id="${node.id}" last="${node.last}">\n${childrenXml}\n${indent}</steps>`; } else if (node.type === 'compref') { const childrenXml = node.children.map(child => generateNode(child, indentLevel + 1)).join('\n'); return `${indent}<compref id="${node.id}" ref="${node.ref}">\n${childrenXml}\n${indent}</compref>`; } else if (node.type === 'step') { return `${indent}<step id="${node.id}" type="${node.typeAttr}">\n${' '.repeat(indentLevel + 1)}<parameterizedString isformatted="true">${node.action}</parameterizedString>\n${' '.repeat(indentLevel + 1)}<parameterizedString isformatted="true">${node.result}</parameterizedString>\n${indent}</step>`; } return ''; } return generateNode(tree); } // 使用示例 const stepsArray = [ {step: 1, action:"Stp 001", result:"Act 001", type:"ValidateStep", id:"2"}, {step: 2, compref: true, result:"ref-89", id:"4"}, {step: 3, action:"Stp 003", result:"Act 003", type:"ValidateStep", id:"3"} ]; const tree = arrayToTree(stepsArray); const xmlStr = treeToXML(tree); console.log(xmlStr);
代码说明
arrayToTree:通过递归索引的方式,将线性数组转换为嵌套树形结构,自动识别compref的子节点层级;treeToXML:遍历树形结构,按缩进生成XML字符串,保留节点的id、type等属性;- 若数组中保留了原XML的
id和type字段,可精准还原原结构;未保留时则用随机id和默认type替代。
内容的提问来源于stack exchange,提问作者Ragul
相关产品推荐
相关产品推荐

