一、两种数据模型的差异
XML 是树 + 属性 + 文本,同一个标签可以重复出现表示列表;JSON 是对象 + 数组,重复靠数组表达,属性只能变成键。所以转换的核心是给「属性」和「文本」找位置。
二、常见映射约定
业界比较通用的约定(如 xml2js 的默认行为)是:
<user id="7" vip="true">
<name>张三</name>
<tags><tag>a</tag><tag>b</tag></tags>
</user>
{
"user": {
"@id": "7",
"@vip": "true",
"name": "张三",
"tags": { "tag": ["a", "b"] }
}
}
规则总结:
- 属性加
@前缀,避免和子元素同名冲突。 - 纯文本节点直接作为值;同时有属性和文本时,文本放
#text。 - 同名重复元素变成数组;只出现一次则是对象——这正是最大的歧义来源。
- 空元素
<a/>通常变成""或null,要按目标系统的习惯统一。
三、单元素歧义怎么处理
<tag>a</tag> 转出来是 {"tag":"a"} 还是 {"tag":["a"]}?取决于约定,而消费方往往写死了其中一种。稳妥做法:
- 转换时显式开启「同名即数组」选项(xml2js 的
explicitArray: true),让所有元素都是数组,消费方不用判断类型。 - 或者在 schema 里声明哪些路径是列表,转换时强制成数组。
不做这一步,代码里就会充满 Array.isArray(x) ? x : [x] 这种补丁。
四、会丢失的信息
- 属性顺序和元素顺序(JSON 对象无序,虽然多数实现保留插入顺序)。
- 命名空间前缀:
<ns:user xmlns:ns="...">里的ns在 JSON 里通常被拍平或丢弃,跨系统校验签名时会出问题。 - 注释、处理指令、DTD。
- CDATA:
<![CDATA[<p>hi</p>]]>内容会被当纯文本,转回 XML 时若不重新包 CDATA,尖括号会被当成标签。
如果 XML 要用于签名、报文校验,不建议转 JSON 再转回来——往返一次信息就可能不完整。要么全程用 XML 处理,要么只做单向转换。
五、实际转换的注意点
- 编码声明:XML 头部的
<?xml version="1.0" encoding="GBK"?>决定字节如何解析,读文件时必须按它来,不能一律 UTF-8。 - 实体:
&<在解析时会被还原,转成 JSON 后就是普通字符;反向转换时要重新转义,否则生成的 XML 非法。 - 大文件:几十 MB 的 XML 不要一次性 DOM 解析,用流式(SAX/StAX)逐个节点处理。
- 数字与布尔:XML 里一切都是字符串,
"true"转成 JSON 后是否要变成true,取决于消费方,别默认转换。
六、工具选择
小段报文可以直接贴进 JSON 格式化校验 检查转换结果是否合法;如果 XML 里带大量实体,用 HTML 实体编解码 单独看转义结果更直观。转换逻辑本身建议写在代码里(Python 的 xmltodict、JS 的 fast-xml-parser),在线工具只用于快速验证。
小结
属性加 @、文本用 #text、重复元素转数组;单元素是否成数组必须提前约定;命名空间和 CDATA 在转换中容易丢,涉及签名校验时不要往返转换。