You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 22:15:53