XML 转 JSON:单节点与多节点的数组形状推断
XML 允许同名兄弟元素,JSON 却要在对象属性和数组之间作出选择。当 <item> 只有一个时输出对象、出现两个时输出数组,调用方就会遇到形状不稳定的问题。本文只讨论单节点/多节点的数组策略,不讨论命名空间或 XML 解析性能。
一、问题概述:同一个元素会产生两种 JSON 形状
<items><item>book</item></items> 可以被转换成 {"item":"book"},而增加第二个 <item> 后又可能变成 {"item":["book","pen"]}。消费者若直接调用 map,单节点结果就会报错;若总是取下标,数组结果又可能丢掉数据。
二、最小复现:单个和多个同名节点
下面的输入已经被表示为同名 XML 节点列表;重点是投影策略,而不是某个 XML parser 的 API。
type XmlNode = { name: string; text: string };
const one: XmlNode[] = [{ name: "item", text: "book" }];
const many: XmlNode[] = [{ name: "item", text: "book" }, { name: "item", text: "pen" }];
console.log(one.length, many.length); // 1, 2
// An object-or-array converter changes shape when the count changes.三、根因:XML 的重复元素没有天然的 JSON 约定
XML schema 可以允许 maxOccurs 大于 1,也可以只允许一次;JSON 转换器如果没有 schema,只能根据当前样本猜测。猜测还会受到空元素、混合内容和同名不同上下文的影响,因此“单个就对象”不是普遍标准。
四、方案比较:始终数组、schema 驱动与后处理
始终数组最容易让调用方编程,但会改变只含一个值时的输出契约;schema 驱动可以按 maxOccurs 生成稳定形状,却要求拿到可信 schema;后处理适合兼容旧接口,但必须记录哪些路径被归一化,不能在业务代码里到处写特判。
五、完整代码:按策略归一化同名节点
以下代码展示一个小型后处理函数。always-array 保持数组,singleton 在没有节点时返回 null、一个节点时返回值、多个节点时返回数组;生产转换应把策略放在字段 schema 中。
type XmlNode = { name: string; text: string };
type ArrayPolicy = "always-array" | "singleton";
function projectItems(nodes: XmlNode[], policy: ArrayPolicy): string[] | string | null {
const values = nodes.filter((node) => node.name === "item").map((node) => node.text);
if (policy === "always-array") return values;
if (values.length === 0) return null;
return values.length === 1 ? values[0] : values;
}
console.log(projectItems([{ name: "item", text: "book" }], "always-array")); // ["book"]
console.log(projectItems([{ name: "item", text: "book" }], "singleton")); // "book"六、常见错误方案
只用第一条同名节点会静默丢数据;看到数组就强制取 [0] 会破坏多值字段;在没有 schema 的情况下凭字段名猜测基数,也可能把一次性元素误包装成数组。把 null、空数组和缺失节点混成同一种值,会进一步模糊契约。
七、边界条件:空元素、顺序与嵌套路径
空 <item/> 应该是空字符串、null 还是空对象,要由 schema 决定。同名节点的顺序可能是业务顺序,归一化时不能排序。不同父路径下的 item 也可能有不同基数,策略应绑定完整路径而不是全局标签名。
八、如何验证输出形状
分别测试零个、一个和多个节点,断言 JSON 类型、元素数量和原始顺序。再用 schema 驱动样本验证 minOccurs/maxOccurs 或等价约束,并对旧接口的后处理结果做快照;不要只检查 JSON 是否能被序列化。
九、FAQ
问:始终数组是不是最正确?答:它通常更稳定,但必须与现有 API 契约和消费者预期一致。
问:没有 XSD 能推断数组吗?答:只能根据样本做暂时策略,不能把一次观察当成长期 schema。
问:后处理会不会掩盖 parser 问题?答:会,所以应在归一化前验证节点数量和路径,失败时保留原始上下文。
十、总结
单节点/多节点导致的形状变化来自 XML 重复元素与 JSON 数组之间缺少天然映射。优先选择稳定的始终数组或可信 schema 驱动策略;必须兼容旧结果时再做集中后处理,并验证零个、一个、多个节点及顺序边界。
来源与延伸阅读
技术审校所依据的规范与权威参考资料。
相关文章
XML 转 JSON:浏览器 DOMParser 的解析、错误检测与非流式边界
说明浏览器 DOMParser 的 XML 解析流程、parsererror 检测和 DOM 遍历转 JSON 方法,不编造性能数据,并明确 DOMParser 不是流式解析器。
踩坑避坑XML 转 JSON:namespace URI、prefix 与 localName 的区别
用相同 URI 的不同前缀示例说明 XML 命名空间身份由 namespace URI 决定,转换时应保留 URI 与 localName,而不是依赖 prefix。
错误排查XML Formatter:HTML 实体、字符引用与二次转义
区分 XML 实体、数字字符引用、HTML 实体和 CDATA,定位 XML formatter 在解析与序列化之间产生二次转义的环节。
继续阅读
可打开关联的浏览器工具,使用自己的样本验证文中的处理流程。
打开关联工具