在前后端数据交换中,XML仍然广泛存在于老系统接口、配置文件与第三方报文中。当我们需要把XML文件转换成对象数组,并且要和JSON的数组结构保持一致时,核心难点并不在解析本身,而在于如何让XML中隐含的集合语义在JSON里被正确表达为数组类型。

为什么XML和JSON的数组结构会对不齐
XML用重复的同名子元素来表示一组项,例如多个<item>并列放在<list>下,这在语法上完全合法。但很多解析器在把XML转成树结构时,如果只遇到一个<item>,就会把它当成普通对象;遇到多个才生成数组。这种不确定性会让下游代码在遍历时抛出类型错误,因为代码预期拿到的是数组却得到了单个对象。
JSON则不同,数组必须显式用方括号声明。所谓映射,就是要在转换过程中人为规定:某些标签无论出现几次,都映射成数组;标签上的属性要落到对象的字段里;文本内容作为特定字段(如value或text)保存。只有先明确这套规则,转换结果才具备稳定性。
手动递归转换的基础实现
下面给出一个Node.js环境下的手动转换示例,演示如何把包含重复子节点的XML字符串,通过递归遍历转成固定结构的对象数组。这里使用轻量解析库把XML先变成普通JS对象,再按规则重组。
const xml2js = require('xml2js');
// 原始XML,item可能出现一次或多次
const xml = `
<root>
<list>
<item id="1">苹果</item>
<item id="2">香蕉</item>
</list>
</root>
`;
// 强制把指定标签视为数组
function toArray(node) {
if (!node) return [];
return Array.isArray(node) ? node : [node];
}
// 将解析后的xmlObj映射成对象数组
function mapList(xmlObj) {
const listNode = xmlObj.root.list;
// 无论list下有几个item,都转成数组
const items = toArray(listNode.item);
return items.map(function(it) {
return {
id: it.$.id,
name: it._
};
});
}
xml2js.parseString(xml, function(err, result) {
if (err) throw err;
const arr = mapList(result);
console.log(JSON.stringify(arr, null, 2));
});
上面的代码关键在toArray函数:它检查节点是否已经是数组,不是就包一层。这样即使XML里只有一个<item>,输出的JSON也是[{id:'1', name:'苹果'}]这样的数组,而不是单独的对象。属性通过it.$访问,文本通过it._访问,这是xml2js的默认约定。
这种写法的好处是逻辑完全可控,你可以针对不同的标签名写不同的映射函数。缺点是当XML层级变深、字段变多时,手工映射代码会膨胀,且容易遗漏某些边界情况,比如空节点、带命名空间的标签等。
使用专用库做结构映射
如果不想自己维护递归逻辑,可以选择支持显式数组声明的库,例如xml-js或fast-xml-parser。后者允许在配置里直接标记哪些标签是数组,解析阶段就完成结构统一。
const parser = require('fast-xml-parser').XMLParser;
const xml = `
<root>
<list>
<item id="1">苹果</item>
<item id="2">香蕉</item>
</list>
</root>
`;
const options = {
ignoreAttributes: false,
attributeNamePrefix: '@_',
// 明确声明list下的item始终作为数组
isArray: function(tagName) {
return tagName === 'item';
}
};
const obj = parser.parse(xml, options);
const arr = obj.root.list.item.map(function(it) {
return {
id: it['@_id'],
name: it['#text']
};
});
console.log(arr);
fast-xml-parser的isArray回调让映射规则声明式地集中管理,比手写递归更不容易出错。属性前缀和文本字段名都可在配置里自定义,方便对接不同的JSON规范。对于复杂报文,这种方式能显著降低维护成本。
不过要注意,声明过多标签为数组可能影响性能,因为解析器会对每个节点做函数判断。在超大文件场景下,建议只把真正需要数组语义的标签列入白名单,其余保持默认。
映射时的常见坑与处理建议
第一个坑是命名空间。带冒号的标签名如<ns:item>在JSON里若直接当键名,会让前端取值很麻烦。可以在映射阶段把前缀去掉,或者统一成下划线形式,例如ns_item。
第二个坑是空集合。XML里<list></list>表示空列表,但某些解析结果会变成null而非空数组。映射函数里应显式返回[],避免前端调用forEach时报错。第三个坑是混合内容,即一个标签既有文本又有子节点,这时需要约定文本存到哪个字段,否则会和子节点对象合并冲突。建议在团队内统一一份XML到JSON的映射规范文档,减少协作成本。
| 方案 | 可控性 | 维护成本 | 适用场景 |
|---|---|---|---|
| 手动递归 | 高 | 高 | 结构固定、字段少 |
| 专用库声明数组 | 中 | 低 | 复杂报文、多团队对接 |
总结来说,XML转对象数组并不是简单调用解析器就够了,真正的工作在于定义并强制执行数组映射规则。无论选择手写还是用库,只要保证重复元素始终输出数组、属性与文本分字段存储,就能让JSON侧稳定消费数据。
XML_to_JSON对象数组映射XML数组结构修改时间:2026-08-02 14:48:29