Java 生態提供三種主流 XML 解析 API,各有優勢場景:
| 模式 | 特點 | 記憶體 | 適用場景 |
|---|---|---|---|
| DOM | 樹狀模型,一次性載入整個文檔 | 高(佔用大) | 小型 XML、需隨機訪問、頻繁修改 |
| SAX | 事件驅動,流式讀取,觸發回調 | 低(不載入樹) | 大型 XML、單次順序讀取、記憶體受限 |
| StAX | 拉取式流解析,程式主動迭代事件 | 低 | 大型 XML、需精確控制解析流程、雙向讀寫 |
繼承 DefaultHandler 覆寫關鍵回調方法:
public class ConfigParser extends DefaultHandler {
private String currentSection;
public SysConfigItem sysConfig;
public List<InterfaceConfigItem> interfaceList = new ArrayList<>();
// ... 其他列表
@Override
public void startDocument() {
sysConfig = new SysConfigItem();
// 初始化各列表
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("Item".equalsIgnoreCase(qName) && attrs.getLength() > 0) {
// 根據當前區段建立對應對象並加入列表
switch (currentSection) {
case "SysConfigItem": sysConfig = new SysConfigItem(attrs); break;
case "InterfaceConfigItems": interfaceList.add(new InterfaceConfigItem(attrs)); break;
// ...
}
} else {
currentSection = qName; // 記錄進入的區段名
}
}
// endElement、characters 視需求實作
}
public class ConfigItemBase {
private static final DateFormat DF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public ConfigItemBase(Attributes attrs) {
Field[] fields = this.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
String attrVal = attrs.getValue(f.getName());
if (attrVal == null) continue;
try {
String type = f.getType().getSimpleName();
if ("String".equalsIgnoreCase(type)) f.set(this, attrVal);
else if ("Integer".equalsIgnoreCase(type)) f.set(this, Integer.valueOf(attrVal));
else if ("Double".equalsIgnoreCase(type)) f.set(this, Double.valueOf(attrVal));
else if ("Date".equalsIgnoreCase(type)) f.set(this, DF.parse(attrVal));
else System.out.println("Warning: Unhandled field " + f.getName());
} catch (Exception e) { e.printStackTrace(); }
}
}
}
JAXB 以註解驅動,將 XML 直接綁定為 Java 對象,開發效率最高,適合結構固定的業務報文。
報文結構特點:外層 InterBOSS,內層 SvcCont 為 CDATA 包裹的內層 XML。
<?xml version="1.0" encoding="UTF-8"?>
<InterBOSS>
<Version>0100</Version>
<SvcCont><![CDATA[
<?xml version="1.0"?>
<batchSyncOrderRelationReq>...</batchSyncOrderRelationReq>
]]></SvcCont>
</InterBOSS>
@XmlRootElement(name = "batchSyncOrderRelationReq")
@XmlAccessorType(XmlAccessType.FIELD)
public class BatchSyncOrderRelationReq extends BossMessage<BatchSyncOrderRelationReq> {
@XmlElement(name = "msgTransactionID")
private String msgTransactionId;
@XmlElement(name = "reqNum")
private String reqNum;
@XmlElement(name = "reqBody")
private List<BatchSyncOrderRelationReqBody> reqBodyList;
// getter/setter 省略
@Override
public BatchSyncOrderRelationReq Deserialized(String xml) throws BusinessException {
try {
// 1. 解析外層 Head
JAXBContext headCtx = JAXBContext.newInstance(MessageHead.class);
MessageHead head = (MessageHead) headCtx.createUnmarshaller()
.unmarshal(new StringReader(xml));
// 2. 解析 CDATA 內層 Body
JAXBContext bodyCtx = JAXBContext.newInstance(BatchSyncOrderRelationReq.class);
BatchSyncOrderRelationReq body = (BatchSyncOrderRelationReq) bodyCtx.createUnmarshaller()
.unmarshal(new StringReader(head.getSvcCont().trim()));
body.setHead(head);
return body;
} catch (JAXBException e) {
throw new BusinessException("反序列化失敗", e);
}
}
}
@XmlAccessorType(XmlAccessType.FIELD)
public class BatchSyncOrderRelationReqBody {
@XmlElement(name = "oprNumb") private String oprNumb;
@XmlElement(name = "subscriptionInfo") private SubscriptionInfo subscriptionInfo;
// 建構子、getter/setter
}
@XmlAccessorType(XmlAccessType.FIELD)
public class SubscriptionInfo {
@XmlElement(name = "oprTime") private String oprTime;
@XmlElement(name = "actionID") private String actionId;
// ... 其餘 20+ 欄位皆用 @XmlElement 映射
}
@XmlRootElement:標識根元素,name 對應 XML 標籤名。@XmlAccessorType(XmlAccessType.FIELD):直接綁定字段,無需寫 getter/setter 也能序列化。@XmlElement(name="..."):指定 XML 子元素名稱,支援集合自動解析為 List。@XmlAttribute:映射 XML 屬性(本例未用)。JAXBContext 建立開銷大,建議單例緩存。@XmlSchema 包級註解。JAXBException 包裝為業務異常,保持介面乾淨。