在异构系统对接中,XML依靠自描述标签和严格的层次结构,能够把业务数据以与平台无关的方式传递给对方。无论是老牌银行接口还是工业设备通信,都能看到它的身影。下面通过具体代码说明它如何完成一次完整的数据交换。

一、为什么选择XML做数据交换
不同语言编写的系统对内存结构和类型定义差异很大,直接共享对象几乎不可能。XML把数据变成纯文本,用标签表达字段含义,接收方只要按约定解析就能还原结构。它支持自定义命名空间,可以在同一个文件里混合多种业务标准,而不产生冲突。
相比单纯用逗号分隔的文本,XML能表达嵌套关系。比如一张订单里包含多个商品,用子节点就能自然体现,而不必约定复杂的分隔符。许多历史系统只认XML,新服务也必须输出对应格式才能联通。
二、一份典型的交换报文
假设电商系统要向仓储系统推送订单,报文需要携带订单号、用户信息和商品列表。下面是一段符合常见约定的XML示例,注意所有特殊字符都已转义,方便网络传输。
<?xml version="1.0" encoding="UTF-8"?>
<order id="2024050001" xmlns="http://ipipp.com/order">
<customer>
<name>张三</name>
<phone>13800000000</phone>
</customer>
<items>
<item>
<sku>A100</sku>
<count>2</count>
</item>
<item>
<sku>B200</sku>
<count>1</count>
</item>
</items>
</order>
这段报文根节点order带有编号与命名空间,内部customer和items分别描述买家和商品。解析器通过路径即可提取内容,不需要额外协议说明。
三、Java端使用DOM解析示例
Java标准库自带DOM工具,适合对小型报文做随机读取。下面的代码演示如何加载上述XML并输出商品总数。
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import java.io.File;
public class OrderReader {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从本地文件加载,实际中可改为InputStream
Document doc = builder.parse(new File("order.xml"));
NodeList items = doc.getElementsByTagName("item");
System.out.println("商品行数:" + items.getLength());
}
}
DOM会把整个文档读入内存,形成树状结构,因此代码里直接用getElementsByTagName就能拿到所有商品节点。对于偶尔交换且体量不大的场景,这种方式写起来直观,也易于调试。
不过当报文达到几十MB时,DOM占用内存会明显上升。此时可换用SAX或StAX按事件流处理,但代码复杂度也会增加。选择哪种解析器,取决于实际数据规模与硬件条件。
四、Python端生成报文示例
Python内置xml.etree.ElementTree模块,可以很方便地拼装出同样的订单结构,便于模拟发送端。
import xml.etree.ElementTree as ET
order = ET.Element("order", {"id": "2024050001"})
cust = ET.SubElement(order, "customer")
ET.SubElement(cust, "name").text = "张三"
ET.SubElement(cust, "phone").text = "13800000000"
items = ET.SubElement(order, "items")
for sku, count in [("A100", 2), ("B200", 1)]:
item = ET.SubElement(items, "item")
ET.SubElement(item, "sku").text = sku
ET.SubElement(item, "count").text = str(count)
tree = ET.ElementTree(order)
tree.write("order.xml", encoding="UTF-8", xml_declaration=True)
上述脚本运行后生成的文件与前面展示的报文结构一致。用子元素逐步挂载的方式,比手动拼接字符串更安全,能自动处理特殊字符转义,避免写出非法XML。
如果接收方要求带命名空间,只要在Element构造时传入xmlns属性即可,但解析端也要对应识别,否则会出现取不到节点的尴尬。这也是联调时最容易忽略的一点。
五、常见误区与注意点
不少人在写XML时把<或&直接放进文本节点,导致解析报错。正确做法是对这类字符转义,或者把整段内容包进CDATA区。另外编码声明写了UTF-8,文件却以GBK保存,也会让对方读取乱码。
命名空间看似可选,但在对接标准接口时往往是必填。双方应提前确认前缀与URI,并在代码中统一使用,防止出现“明明标签名一样却匹配不到”的问题。把这些细节处理好,XML数据交换就能稳定服务于各类跨系统业务。