雖然網上有很多類似的文章,可都描述的不是特別清楚且都是很老的文章了,讓人走很多彎路,這裏完整記錄下。
說在前面
網上大多數分析的帖子都說dom4j解析xml性能最好,所以在碰到實際業務場景中就着手使用dom4j來解析xml了。
在業務場景中解析xml基本上兩種,一種是配置,另一種是調用外部項目接口反饋的xml。前者這裏不多說,自己的配置隨心所欲,通常xml的結構也相對比較簡單。而後者就比較糟心了,比如我遇到的,一邊接對應的接口一邊不停的吐槽,淚崩中啊。
至於dom4j如何使用和一些基本概念,這裏就不過多描述,網上隨便一搜就是一大堆。
這裏主要說下解析含有命名空間的XML。
具體實現
先看要我要解析的XML格式,如下截圖,其實也不是很複雜:
截圖1
在瞭解完dom4j基本概念之後,我就開始着手開發了,發現在獲取完根節點之後,我需要遞歸幾次才能獲取我需要的record
這個節點,顯然比較麻煩。
於是繼續google發現可以使用selectNode(xpath)
的方式來直接獲取,這個纔是我想要的。
原以爲幾行代碼輕鬆搞定,可最後發現selectNode
始終獲取不到對應的節點,起初還以爲是我的路徑有問題,後來才知道dom4j不能識別帶命名空間的節點,所以在讀取帶命名空間的XML時,要在每個節點前加上命名空間。
好吧,我只想安安靜靜地解析個XML,居然這麼繞,頓時心裏又在吐槽這個接口本身了,非要用什麼webservice返回個xml,http+json多好呀。
吐槽歸吐槽,接還是得接啊,網上找些資料之後也大致明白,只要在節點前加上命名空間即可。
可好事多磨啊,我接的那個接口居然有兩個命名空間,soapenv
和response
兩個節點上都有,好吧,我忍。
大體思路就是,先獲取根節點,取到對應的命名空間,然後selectSingleNode到response
這個節點取第二個命名空間,最後再組裝xpath取到自己想要的節點。核心代碼如下:
Document doc = XmlUtil.strToDocument(responseStr); Map map = new HashMap(); // 獲得命名空間 String firstUrl = doc.getRootElement().getNamespaceURI(); map.put("firstUrl", firstUrl); XPath x = doc.createXPath("//firstUrl:Body"); x.setNamespaceURIs(map); //獲取第二層xml的命名空間 String secendUrl = ((Element) x.selectSingleNode(doc)).element("response").getNamespaceURI(); map.put("secendUrl", secendUrl); x = doc.createXPath("//firstUrl:Body/secendUrl:response/" + "secendUrl:responseBody/secendUrl:records/secendUrl:record"); x.setNamespaceURIs(map); List<Element> nodes = x.selectNodes(doc);
說在後面
在開發一段時間的java之後,突然開始懷念起 .net了,尤其在處理一些細節方面的時候,總感覺java有點囉嗦,明明可以一行代碼搞定的事情,需要寫個三四行。
java10終於支持var關鍵字了,java11要開始收費了,不懂是福還是禍啊~