XML 转 JSON 实战:字段映射陷阱与命名空间 / 混合内容处理策略

为什么 XML 转 JSON 不是”换个写法”

JSON 与 XML 同为树形结构数据格式,但二者在语义表达能力上并不对等。XML 拥有 JSON 缺失的三类能力:

  1. 属性 vs 子元素的二元性:XML 元素可以同时拥有属性与子元素,而 JSON 对象的 key 必须唯一
  2. 混合内容(mixed content):XML 元素内部可以混合文本与子元素(如 <p>正则 <code>\d+</code> 是数字</p>),JSON 没有原生表达
  3. 顺序敏感的子节点: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 决策清单

  • 属性与子元素可能同名 → 必须启用前缀
  • 属性均为元数据(如 idversionxmlns)→ 可考虑扁平化丢弃
  • 输出将用于 Java / .NET 反序列化 → 优先 @ 前缀(与 Jackson @JacksonXmlProperty 默认行为一致)
  • 输出将用于前端 JS 处理 → 可用 _ 前缀,避免方括号访问

二、命名空间:prefix 映射与 @xmlns 处理

2.1 命名空间的本质

XML 命名空间用于消除标签同名歧义,例如 SOAP 消息中 soap:Envelopexsd:schemaEnvelopeschema 都来自不同命名空间:

<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.isArrayforEach 处理,无需类型判断。

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>nullnull 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 服务响应解析

  1. XML 转 JSON 工具 粘贴 SOAP XML
  2. 配置:保留 prefix + 启用类型推断 + 子元素始终为数组
  3. 复制 JSON 结果
  4. JSON 格式化工具 验证结构
  5. JSONPath 查询工具 提取目标字段

9.2 工作流 2:Office 文档元数据提取

  1. 解压 docx(实质是 ZIP),提取 word/document.xml
  2. XML 转 JSON 工具 转换为 JSON
  3. 配置:保留命名空间 + 子元素始终为数组
  4. JSONPath 查询工具 查询特定节点

9.3 工作流 3:RSS Feed 转换为结构化数据

  1. XML 转 JSON 工具 粘贴 RSS XML
  2. 配置:丢弃命名空间 + 子元素始终为数组
  3. 复制 JSON 结果
  4. CSV 与 JSON 互转工具 转换为 CSV 便于 Excel 查看

十、最佳实践清单

  1. 属性 vs 子元素同名 → 必须启用 @ 前缀策略,避免 key 冲突
  2. SOAP / WSDL / XSD 文档 → 保留 prefix,与原 XML 视觉一致
  3. RSS / Atom Feed → 丢弃命名空间,简化输出
  4. 下游 TypeScript / Java 代码 → 启用”子元素始终为数组”,类型一致
  5. 含 ID / 邮编 / 电话号 / 金额字段 → 关闭类型推断,保留字符串
  6. 大整数(>2^53)字段 → 关闭类型推断,避免精度丢失
  7. 含 CDATA 内容 → 评估是否需要分离模式保留边界
  8. 含混合内容 → 评估是否需要节点序列保真
  9. XXE 防护 → 优先使用基于 DOMParser 的工具,避免服务端 DTD 解析
  10. 跨工具协同 → 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 数据处理场景。