XML 未死(不幸地)——像成年人一样将其转换为 JSON
你以为你逃过了XML。你错了。这里教你如何处理来自旧式API和企业系统的XML,理解其结构上的怪异之处,并将其转换为JSON,而不会失去理智。
你学会了REST,拥抱了JSON,认为XML时代已经结束,就像Flash或IE6那样,你会像篝火故事一样警告初级开发者。但你的新客户给了你银行集成的API凭证,结果你看到了一个长达400行的SOAP信封,直直地盯着你。
欢迎回到XML。它从未离开。
为什么XML无处不在
对于许多构建现代应用的开发者来说,XML看起来像是一种过时的技术。但在现实世界中——尤其是在企业软件、银行、医疗保健和政府系统中——XML是绝对关键的基础设施。你可能会在以下这些场景中遇到它:
- SOAP Web服务 – 金融机构、保险平台和大型ERP系统仍使用SOAP作为标准。你的金融科技集成很可能需要经过一个。
- 银行API – ISO 20022、SWIFT消息以及许多核心银行API均以XML格式通信。目前没有REST选项。
- 政府数据 – HMRC、IRS以及大多数政府门户网站都接受或返回XML格式。这一趋势短期内不会改变。
- 企业中间件 – SAP、Oracle以及老旧的ESB系统原生支持XML。如果你正在集成任何企业级系统,那么你很可能遇到XML。
- RSS和Atom数据流 – 仍然使用XML。许多内容管道、新闻聚合器和监控工具都依赖它们。
令人不安的真相:XML不会消失,因为建立在它之上的系统不会消失。替换一家银行的核心基础设施不是一次冲刺任务。因此,你只能适应。
XML与JSON:真正让你出错的结构差异
在你开始转换之前,理解为什么XML到JSON不仅仅是格式转换非常重要。这两种格式在数据建模上存在差异,这些差异会引发真实的问题。
并列对比:两种格式中的相同数据
以一个简单的客户订单为例。这里是XML格式:
<order id="ORD-1042" currency="GBP">
<customer>
<name>Alice Martin</name>
<email>alice@example.com</email>
</customer>
<items>
<item sku="PRD-001">
<description>Wireless Keyboard</description>
<quantity>1</quantity>
<price>49.99</price>
</item>
<item sku="PRD-007">
<description>USB-C Hub</description>
<quantity>2</quantity>
<price>29.99</price>
</item>
</items>
</order>
这里是JSON格式:
{
"order": {
"@id": "ORD-1042",
"@currency": "GBP",
"customer": {
"name": "Alice Martin",
"email": "alice@example.com"
},
"items": {
"item": [
{
"@sku": "PRD-001",
"description": "Wireless Keyboard",
"quantity": "1",
"price": "49.99"
},
{
"@sku": "PRD-007",
"description": "USB-C Hub",
"quantity": "2",
"price": "29.99"
}
]
}
}
}
你已经可以看到结构上的摩擦点。我们来逐一分析。
属性与键
XML元素可以携带属性(id="ORD-1042")以及子元素和文本内容。JSON没有属性的概念——所有内容都是键值对。最常见的约定是在转换时将属性前缀为 @ ,从而得到 "@id": "ORD-1042"。一些解析器使用 $ 或完全扁平化处理。这种约定很重要,因为你的消费代码需要知道预期的前缀。
数组与重复元素
这一点经常让开发者遇到麻烦。在JSON中,数组是显式的: [...]。在XML中,没有这种区别——重复的兄弟元素被隐式视为列表。一个解析器看到一个 <item> 元素时,可能返回一个对象。看到两个 <item> 元素时,它会返回一个数组。当API返回单个结果时,你的代码就会崩溃。
解决方法是为已知的列表字段强制使用数组,或使用能保留类型信息的库。当你处理单个数据时,务必检查某个字段看起来像单个对象,但在生产数据中可能有时是列表。
文本节点与混合内容
XML元素可以同时包含文本内容和子元素(混合内容)。JSON无法清晰地表示这一点。解析器处理方式不同——有些使用 #text 键,有些使用 _,有些则直接丢弃混合内容。如果你在转换带有混合内容的XML,请手动验证输出结果。
命名空间
SOAP响应中充满了XML命名空间: <ns2:getOrderResponse xmlns:ns2="http://...">。根据你的解析器,这些命名空间要么被剥离,要么被合并到键名(ns2:getOrderResponse),要么映射到URI。大多数情况下你希望它们被剥离,但如果两个命名空间共享元素名称,你将失去区分。在处理之前,了解你所面对的内容。
快速路径:无需编写解析器即可在线转换XML
如果你在调试API响应、探索一个不熟悉的XML模式,或进行一次性转换,编写解析器是过度的。使用 XML 到 JSON 转换器 ——粘贴你的XML,立即获得干净的JSON,并在编写任何代码前检查结构。
它处理属性(使用 @ 前缀约定)、嵌套元素、重复元素作为数组,并保留文本内容——这涵盖了大多数真实世界的API数据。它有助于快速理解剥离信封后的SOAP响应实际结构。
程序化转换:生产环境中的最佳选择
在生产环境中集成XML API时,你应该使用成熟的库而不是手动编写解析器。以下是各语言的推荐选项:
JavaScript / Node.js
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '@',
isArray: (name) => ['item', 'product', 'order'].includes(name),
});
const result = parser.parse(xmlString);
fast-xml-parser 是Node.js的最佳选择。使用 isArray 强制处理已知列表元素为数组——这可以防止在生产环境中出现单个项与数组不一致的问题。
Python
import xmltodict
with open('response.xml') as f:
data = xmltodict.parse(f.read())
import json
print(json.dumps(data, indent=2))
xmltodict 是Python的标准选择。它使用 @ 作为属性前缀,使用 #text 作为文本节点。请注意,它返回一个 OrderedDict,该结构可以与 json.dumps.
PHP
$xml = simplexml_load_string($xmlString);
$json = json_encode($xml);
$data = json_decode($json, true);
良好序列化。 simplexml_load_string + json_encode PHP的 DOMDocument 是快速路径,但它对属性处理不一致,且在边缘情况中可能丢失数据。在处理SOAP响应的生产环境中,建议使用 LIBXML_NOBLANKS 配合
或专用库。
- 常见陷阱需注意 数字被转换为字符串。
"49.99"XML没有数字类型——所有内容都是文本。你的价格字段将是49.99,而不是 - 。转换后必须显式转换。
<active>true</active>变成"active": "true"布尔值仍为字符串。 - 。在使用前需检查。
null空元素变为<middleName/>或空对象。null,"", 或者{}可能被转换为 - ,具体取决于解析器。需测试边缘情况。 CDATA段
- 可能被保留也可能被丢弃。如果API使用CDATA来转义HTML内容,需确认你的解析器是否处理它,避免内容被静默移除。 顺序无法保证。
某些模式中XML元素的顺序是重要的;而JSON对象的键顺序则不是。如果消费系统依赖顺序,必须显式处理。
专门处理SOAP <Envelope> SOAP在XML之上增加了一层:每个响应都被包裹在一个 <Body>中,带有 <Header> ,通常带有命名空间声明和一个
块。在转换为JSON之前,通常需要提取其中的主体内容。 zeep 在Python中使用 soap 且 strong-soap (SOAP客户端),你可以直接获得Python对象,无需自行解析XML。在Node.js中,也应如此操作。如果你直接使用 fetch 或 axios调用SOAP端点,你将需要手动剥离信封,然后再运行XML到JSON转换器。
为了快速查看SOAP响应, XML 到 JSON 转换器 非常有用——粘贴完整的信封,查看完整结构,从而确定你需要的数据路径。
接受现实并继续前进
当你意识到你的新项目必须集成一个2003年开发的SOAP API时,会有一种特别的痛苦。允许自己感受这种痛苦,然后继续前进。XML是一个已解决的问题——解析器成熟,转换工具存在,陷阱也已被充分记录。你不是第一个面对400行信封的开发者。
使用适合你语言的库,显式处理类型转换,为已知列表字段强制使用数组,并使用真实数据而非API文档中的理想化示例进行测试。文档会展示一个条目;生产环境会发送你五十个。
对于探索性工作——理解一个不熟悉的XML模式,验证转换逻辑在编写代码前——请将 XML 到 JSON 转换器 保存到书签中。它比每次都需要启动脚本要快得多。
