
本文详解如何在 JavaScript 中实现健壮的 XML→JSON 转换,重点解决属性(如 id)被错误挂载到父节点而非所属元素的问题,并提供可直接运行的优化代码与关键设计原则。
本文详解如何在 javascript 中实现健壮的 xml→json 转换,重点解决属性(如 `id`)被错误挂载到父节点而非所属元素的问题,并提供可直接运行的优化代码与关键设计原则。
在将 XML 结构转换为 JSON 时,一个常见却极易被忽视的陷阱是:XML 元素的属性未被正确归属到其对应节点对象中,反而“泄漏”至父级或顶层对象。原始实现中,
根本原因在于原始逻辑中属性收集与子节点递归处理的顺序和作用域错位:它在遍历子元素时才检查并设置当前子项的属性,但此时已脱离该子项自身上下文;更严重的是,当多个同名元素(如多个
✅ 正确方案的核心原则是:每个节点的属性必须在该节点自身的转换作用域内完成初始化,并优先于子节点处理。以下是经过验证的优化实现:
function xml2js(node) {
try {
// ✅ 第一步:为当前节点创建空对象,并立即注入全部属性(若存在)
const obj = [...(node.attributes || [])].reduce((acc, attr) => {
acc[attr.nodeName] = attr.value;
return acc;
}, {});
// ✅ 第二步:判断是否为纯文本节点(无子元素)
if (node.children.length === 0) {
// 保留原始文本内容(含 CDATA),避免丢失 HTML 片段等富内容
obj.content = node.textContent.trim();
} else {
// ✅ 第三步:遍历子元素,递归构建结构
for (const child of node.children) {
const { nodeName } = child;
const existing = obj[nodeName];
if (existing === undefined) {
// 首次出现:直接赋值为递归结果
obj[nodeName] = xml2js(child);
} else {
// 已存在:转为数组并追加(支持重复标签,如多个 <topic>)
if (!Array.isArray(existing)) {
obj[nodeName] = [existing];
}
obj[nodeName].push(xml2js(child));
}
}
}
return obj;
} catch (e) {
console.error('XML to JSON conversion error:', e);
throw e;
}
}
// 使用示例
const XMLString = `<?xml version="1.0" encoding="UTF-8"?><smf><lasttime>1694246241</lasttime><topic id="78603"><icon>@@##@@]]></icon><subject><a href="...">Test</a></subject><lastpost><time ts="1694246241">Today at 12:57 AM]]></time></lastpost></topic></smf>`;
const XMLDoc = new DOMParser().parseFromString(XMLString, 'text/xml');
const result = xml2js(XMLDoc.documentElement); // 注意:传入 documentElement 而非整个文档
console.log(JSON.stringify({ smf: result }, null, 2));</topic>
? 关键改进点说明:
- 属性前置注入:[...node.attributes] 确保每个节点对象在处理任何子节点前,已完整承载自身所有属性(如 id, ts),彻底避免跨层级污染。
- textContent 安全使用:对无子元素节点,统一用 textContent 提取内容(自动合并文本节点与 CDATA),并 .trim() 清理空白,兼顾语义与实用性。
-
数组化逻辑健壮性:通过 Array.isArray() 显式判断 + 动态转数组,精准处理重复标签(如多
),无需预判结构。 -
调用入口修正:务必传入 XMLDoc.documentElement(即
节点),而非整个 XMLDoc 对象,否则会额外包含 声明等非预期节点。
⚠️ 注意事项:
- 本方案不自动展开 #text 或注释节点,聚焦于标准元素与属性,符合大多数业务场景需求;如需保留注释,需额外检查 node.nodeType === Node.COMMENT_NODE。
- 若 XML 含命名空间(xmlns),需扩展属性过滤逻辑(如跳过 xmlns*),避免污染 JSON 结构。
- 对超大 XML,建议增加递归深度限制或改用流式解析(如 sax-js),防止栈溢出。
通过以上重构,











