为什么 XML 转 JSON 不是”换个写法”
JSON 与 XML 同为树形结构数据格式,但二者在语义表达能力上并不对等。XML 拥有 JSON 缺失的三类能力:
- 属性 vs 子元素的二元性:XML 元素可以同时拥有属性与子元素,而 JSON 对象的 key 必须唯一
- 混合内容(mixed content):XML 元素内部可以混合文本与子元素(如
<p>正则 <code>\d+</code> 是数字</p>),JSON 没有原生表达 - 顺序敏感的子节点:XML 子节点顺序是文档语义的一部分(如
<step>1</step><step>2</step>),JSON 对象的 key 顺序在多数引擎中不保证
正因为这三种能力差异,“把 XML 转 JSON” 在工程上不存在”完美”映射方案,所有工具(包括本站的 XML 转 JSON 工具)都是在多个候选策略间做取舍。本文系统梳理这些策略,并给出按场景选择配置的决策清单。
一、属性 vs 子元素:何时需要 @ 前缀
1.1 冲突场景
XML 元素允许属性与子元素同名:
<user id="1">
<id>100</id>
<name>Aether</name>
</user>
如果不加前缀直接转为 JSON:
{ "user": { "id": ??? } } // 属性 id 与子元素 id 冲突
JSON 对象 key 必须唯一,必须做选择:保留属性、保留子元素,或用前缀区分。
1.2 主流前缀策略对比
| 策略 | 示例 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
@ 前缀(推荐) | {"@id": "1", "id": "100"} | 符合 FastXML 约定 与 BadgerFish 规范,业界主流 | 属性名带 @ 在 JS 中需用方括号访问(obj["@id"]) | 通用场景、属性与子元素共存 |
_ 前缀 | {"_id": "1", "id": "100"} | JS 友好(obj._id),与常见命名规范一致 | 不直观,需文档说明 | Java 项目、MongoDB 生态 |
$ 前缀 | {"$id": "1", "id": "100"} | 与 XPath $var 视觉区分 | 与 jQuery / MongoDB 查询语法冲突 | 极少使用 |
| 属性优先丢弃 | {"id": "100"} | 结构最简洁 | 信息丢失(属性 id="1" 消失) | 仅展示文本场景 |
| 子元素优先丢弃 | {"id": "1"} | 属性保留 | 子元素丢失 | 极少使用 |
本站 XML 转 JSON 工具 默认采用 @ 前缀策略,与 FastXML 约定一致,便于跨工具互转。
1.3 决策清单
- 属性与子元素可能同名 → 必须启用前缀
- 属性均为元数据(如
id、version、xmlns)→ 可考虑扁平化丢弃 - 输出将用于 Java / .NET 反序列化 → 优先
@前缀(与 Jackson@JacksonXmlProperty默认行为一致) - 输出将用于前端 JS 处理 → 可用
_前缀,避免方括号访问
二、命名空间:prefix 映射与 @xmlns 处理
2.1 命名空间的本质
XML 命名空间用于消除标签同名歧义,例如 SOAP 消息中 soap:Envelope 与 xsd:schema 的 Envelope 与 schema 都来自不同命名空间:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetStockPrice xmlns:m="http://example.org/stock">
<m:StockName>IBM</m:StockName>
</m:GetStockPrice>
</soap:Body>
</soap:Envelope>
JSON 没有命名空间概念,转为 JSON 时有两类处理策略。
2.2 保留 prefix 策略(默认)
将 prefix 作为标签名前缀保留:
{
"soap:Envelope": {
"@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
"soap:Body": {
"m:GetStockPrice": {
"@xmlns:m": "http://example.org/stock",
"m:StockName": "IBM"
}
}
}
}
优点:与原 XML 视觉一致,便于对照
缺点:标签名带 : 在多数编程语言中不是合法标识符,需用方括号访问
2.3 命名空间折叠策略
部分工具支持”将 xmlns 折叠为命名空间属性”的选项,将 prefix 与 URI 配对存入独立字段:
{
"Envelope": {
"@xmlns": { "soap": "http://schemas.xmlsoap.org/soap/envelope/" },
"Body": {
"GetStockPrice": {
"@xmlns": { "m": "http://example.org/stock" },
"StockName": "IBM"
}
}
}
}
优点:标签名干净,命名空间信息完整
缺点:丢失 prefix 信息(无法还原原 XML 的 soap:Envelope 写法)
2.4 完全丢弃命名空间策略
适用于”只关心数据本身,不关心命名空间归属”的场景:
{
"Envelope": {
"Body": {
"GetStockPrice": { "StockName": "IBM" }
}
}
}
优点:JSON 最干净,前端处理最简单 缺点:完全不可逆,且可能产生标签同名冲突(不同命名空间下同名元素被合并)
2.5 决策清单
- SOAP / WSDL / XSD 文档 → 保留 prefix(与原 XML 视觉一致)
- RSS / Atom Feed → 可丢弃命名空间(数据本身无歧义)
- 自定义命名空间的文档 → 谨慎选择,保留更安全
- 输出需可逆重建 XML → 必须保留
@xmlns属性
三、同名子元素合并数组的语义保真
3.1 单元素 vs 多元素的歧义
XML 中”是否重复出现”是文档语义的一部分:
<tags>
<tag>前端</tag>
</tags>
与
<tags>
<tag>前端</tag>
<tag>工具</tag>
</tags>
转 JSON 时,前者 "tag": "前端" 是字符串,后者 "tag": ["前端", "工具"] 是数组。类型不一致是下游处理的常见 bug 来源:
// 错误示例:未考虑单元素场景
data.tags.tag.forEach(t => console.log(t));
// 单元素时 tag 是字符串,forEach 报错
3.2 三种处理策略
| 策略 | 单元素输出 | 多元素输出 | 优点 | 缺点 |
|---|---|---|---|---|
| 默认(智能) | "tag": "前端" | "tag": ["前端", "工具"] | 输出简洁 | 类型不一致 |
| 子元素始终为数组 | "tag": ["前端"] | "tag": ["前端", "工具"] | 类型一致,下游可统一 forEach | 单元素也包数组,冗余 |
| 仅重复时数组 | 同上 | 同上 | 同默认 | 同默认 |
本站 XML 转 JSON 工具 提供了”子元素始终为数组”选项,启用后单元素也会包成数组,下游代码可统一用 Array.isArray 后 forEach 处理,无需类型判断。
3.3 决策清单
- 下游 TypeScript / Java 强类型代码 → 启用”子元素始终为数组”,类型一致
- 仅做展示展示 → 默认即可,输出更简洁
- 需要 1:1 还原 XML 结构 → 启用”子元素始终为数组”
四、CDATA 与混合内容处理
4.1 CDATA 的本质
CDATA 用 <![CDATA[...]]> 包裹原样文本,常用于嵌入含 <、>、& 的内容(HTML 片段、代码、正则)。XML 解析器不会解析 CDATA 内部的标签。
<content><![CDATA[<script>alert(1)</script>]]></content>
4.2 合并 vs 分离策略
| 策略 | CDATA 输出 | 适用场景 |
|---|---|---|
| 合并(默认) | "content": "<script>alert(1)</script>" | 仅关心最终文本,不关心 CDATA 边界 |
| 分离 | "content": { "#cdata": "<script>alert(1)</script>" } | 需保留 CDATA 边界信息(如反向重建 XML) |
本站 XML 转 JSON 工具 默认采用合并策略,可在选项中切换为分离模式。
4.3 混合内容(mixed content)的难题
混合内容指元素内部同时包含文本与子元素,例如:
<p>正则 <code>\d+</code> 是数字</p>
这是 XML 独有的能力,JSON 没有原生表达。三类处理方式:
| 方式 | 输出 | 评价 |
|---|---|---|
| 文本拼合 | "p": "正则 \\d+ 是数字" | 丢失结构,子元素标签信息丢失 |
| 子元素分离 | "p": { "#text": "正则 是数字", "code": "\\d+" } | 丢失文本与子元素的顺序信息 |
| 节点序列 | "p": [{"#text": "正则 "}, {"code": "\\d+"}, {"#text": " 是数字"}] | 顺序保真,但结构复杂 |
工程实践中,节点序列是唯一能 1:1 还原混合内容的方式,但 JSON 体积膨胀严重,多数工具不采用。本站 XML 转 JSON 工具默认采用 文本拼合 策略,适用于多数场景(混合内容在配置类 XML 中极少见,多见于 HTML 类文档)。
4.4 决策清单
- SOAP / RSS / Spring 配置 → 默认合并即可,CDATA 内多为单文本
- 含 HTML 内容的 XML(如 Atom Feed 的 content 元素)→ 考虑分离模式保留 CDATA 边界
- 需反向重建 XML → 启用分离模式 + 节点序列(如工具支持)
五、注释与处理指令的取舍
5.1 注释
XML 注释 <!-- ... --> 在多数场景下不影响数据语义,可安全丢弃。但部分场景(如 XSL 样式表、文档型 XML)注释是有意义的内容。
JSON 没有注释语法,转换时必须选择:
- 丢弃(默认)
- 保留为
#comment字段(如 FastXML 约定)
5.2 处理指令
处理指令 <?xml-stylesheet ...?>、<?php ...?> 在转换时通常保留为 #processing-instruction 字段,但多数场景下不影响数据语义。
5.3 决策清单
- SOAP / 数据交换类 XML → 丢弃注释与 PI
- 文档型 XML(DocBook、XSLT、SVG 含样式)→ 保留注释
- 默认工具设置:丢弃(默认)
六、类型推断的副作用
6.1 自动类型推断的便利与陷阱
XML 中所有数据均为字符串,但 JSON 有 string / number / boolean / null 四类基本类型。工具会自动推断:
<user>
<id>100</id>
<active>true</active>
<score>0.95</score>
<note></note>
</user>
推断为:
{
"user": {
"id": 100,
"active": true,
"score": 0.95,
"note": null
}
}
6.2 副作用清单
| 副作用 | 触发场景 | 影响 |
|---|---|---|
| 邮编、电话号前导 0 丢失 | <zip>010000</zip> → 010000(数字)→ 10000 | 数据丢失 |
| ID 字段精度丢失 | <id>9007199254740993</id> 超过 JS Number.MAX_SAFE_INTEGER | 精度丢失 |
| 布尔值假阳性 | <version>true</version> 被推断为 boolean | 语义错误 |
| 空元素歧义 | <note></note> → null | null vs 空字符串语义差异 |
6.3 决策清单
- ID / 邮编 / 电话号 / 金额 → 关闭类型推断,保留字符串
- 数值需要参与下游计算 → 启用推断
- 大整数(>2^53) → 关闭推断,保留字符串
- 默认工具设置:本站 XML 转 JSON 工具默认启用类型推断,可在选项中关闭
七、典型 XML 类型实战
7.1 SOAP 响应解析
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetStockPriceResponse xmlns:m="http://example.org/stock">
<m:Price>34.5</m:Price>
</m:GetStockPriceResponse>
</soap:Body>
</soap:Envelope>
推荐配置:保留 prefix + 启用类型推断
{
"soap:Envelope": {
"@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
"soap:Body": {
"m:GetStockPriceResponse": {
"@xmlns:m": "http://example.org/stock",
"m:Price": 34.5
}
}
}
}
7.2 RSS Feed 解析
<rss version="2.0">
<channel>
<title>示例博客</title>
<item>
<title>文章 1</title>
<link>https://example.com/1</link>
</item>
<item>
<title>文章 2</title>
<link>https://example.com/2</link>
</item>
</channel>
</rss>
推荐配置:丢弃命名空间 + 子元素始终为数组(item 必然多元素)
{
"rss": {
"@version": "2.0",
"channel": {
"title": "示例博客",
"item": [
{ "title": "文章 1", "link": "https://example.com/1" },
{ "title": "文章 2", "link": "https://example.com/2" }
]
}
}
}
7.3 SVG 图形解析
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red"/>
</svg>
推荐配置:保留 @ 前缀属性 + 启用类型推断(数值属性可参与计算)
{
"svg": {
"@xmlns": "http://www.w3.org/2000/svg",
"@width": 100,
"@height": 100,
"circle": {
"@cx": 50, "@cy": 50, "@r": 40, "@fill": "red"
}
}
}
7.4 Office 文档(docx)解析
docx 内部为 Office Open XML 标准,包含大量命名空间与属性。推荐配置:保留命名空间 + 保留属性 + 子元素始终为数组(同类型元素较多)。
八、XXE 防护与安全注意
8.1 XXE 攻击原理
XML External Entity(XXE)攻击通过 <!ENTITY> 引用外部资源(如本地文件、HTTP 资源),在解析时被加载,导致敏感信息泄露或 SSRF:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
8.2 防护策略
本站 XML 转 JSON 工具 基于 DOMParser 实现,DOMParser 不解析 DTD 与外部实体,天然免疫 XXE 攻击。但若你使用其他 XML 解析库(如 Java DOM4J、Python lxml),需显式禁用:
- Java DOM4J:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) - Python lxml:
parser = etree.XMLParser(resolve_entities=False, no_network=True) - .NET XmlDocument:
XmlDocument.Load默认不解析外部实体
8.3 其他 XML 攻击向量
| 攻击 | 描述 | 防护 |
|---|---|---|
| XXE | 外部实体引用 | 禁用 DTD / 实体 |
| Billion Laughs | 实体递归展开导致内存耗尽 | 禁用 DTD / 实体 |
| 外部 DTD | 引用远程 DTD 致 SSRF | 禁用外部 DTD 加载 |
| XML Bomb | 嵌套大量元素致内存耗尽 | 限制解析深度 |
九、与 XML 转 JSON 工具的协同工作流
9.1 工作流 1:SOAP 服务响应解析
- 在 XML 转 JSON 工具 粘贴 SOAP XML
- 配置:保留 prefix + 启用类型推断 + 子元素始终为数组
- 复制 JSON 结果
- 在 JSON 格式化工具 验证结构
- 在 JSONPath 查询工具 提取目标字段
9.2 工作流 2:Office 文档元数据提取
- 解压 docx(实质是 ZIP),提取
word/document.xml - 在 XML 转 JSON 工具 转换为 JSON
- 配置:保留命名空间 + 子元素始终为数组
- 在 JSONPath 查询工具 查询特定节点
9.3 工作流 3:RSS Feed 转换为结构化数据
- 在 XML 转 JSON 工具 粘贴 RSS XML
- 配置:丢弃命名空间 + 子元素始终为数组
- 复制 JSON 结果
- 在 CSV 与 JSON 互转工具 转换为 CSV 便于 Excel 查看
十、最佳实践清单
- 属性 vs 子元素同名 → 必须启用
@前缀策略,避免 key 冲突 - SOAP / WSDL / XSD 文档 → 保留 prefix,与原 XML 视觉一致
- RSS / Atom Feed → 丢弃命名空间,简化输出
- 下游 TypeScript / Java 代码 → 启用”子元素始终为数组”,类型一致
- 含 ID / 邮编 / 电话号 / 金额字段 → 关闭类型推断,保留字符串
- 大整数(>2^53)字段 → 关闭类型推断,避免精度丢失
- 含 CDATA 内容 → 评估是否需要分离模式保留边界
- 含混合内容 → 评估是否需要节点序列保真
- XXE 防护 → 优先使用基于 DOMParser 的工具,避免服务端 DTD 解析
- 跨工具协同 → XML 转 JSON 后用 JSONPath 提取字段、JSON 格式化 验证结构、CSV 与 JSON 互转 导出
总结
XML 转 JSON 不是简单的语法替换,而是一次”语义降维”工程:从 XML 的属性 / 子元素二元性、混合内容、顺序敏感、命名空间等能力,映射到 JSON 的扁平 key-value 结构。所有转换策略都是在多个候选方案中做取舍:
- 属性 vs 子元素:
@前缀是主流约定 - 命名空间:保留 prefix 适合数据保真,丢弃适合简化输出
- 同名子元素:始终为数组是下游强类型代码的福音
- CDATA 与混合内容:合并是默认,分离与节点序列是高保真选项
- 类型推断:便利与陷阱并存,关键字段应关闭推断
- XXE 防护:DOMParser 是首选,服务端解析必须显式禁用 DTD
本站 XML 转 JSON 工具 内置上述所有配置选项,可根据场景灵活切换。配合 JSON 格式化工具、JSONPath 查询工具、CSV 与 JSON 互转工具,可形成完整的”XML → JSON → 提取 → 导出”工作流,覆盖绝大多数 XML 数据处理场景。