前言
我們?cè)赬ML系列上篇中介紹了XML的入門使用奈偏,本篇文章將圍繞dom飞几、sax后室、dom4j缩膝、jaxp、jaxb等眾多概念來(lái)進(jìn)行關(guān)系梳理咧擂,敘述Java在解析XML過(guò)程中經(jīng)歷了哪些階段逞盆,希望能夠讓對(duì)上述概念不清晰的讀者有所幫助。
如果你還沒(méi)閱讀過(guò)本系列的上篇松申,可以從下面的鏈接跳轉(zhuǎn)閱讀:
XML系列(上篇)——XML文件入門詳解
先說(shuō)說(shuō)DOM和SAX
DOM
和SAX
都是對(duì)于解析XML提出的標(biāo)準(zhǔn),是jdom
俯逾、dom4j
等實(shí)際應(yīng)用的存在前提和基石贸桶。
1)DOM
DOM 是 W3C 處理 XML 的標(biāo)準(zhǔn) API,它是許多其它與 XML 處理相關(guān)的標(biāo)準(zhǔn)的基礎(chǔ)桌肴,不僅是 Java皇筛,其它諸如 JavaScript,PHP坠七,MS .NET 等等語(yǔ)言都實(shí)現(xiàn)了該標(biāo)準(zhǔn)水醋。DOM對(duì)于XML文件采用的解析方式是一次性加載整個(gè)XML文檔,在內(nèi)存中形成一個(gè)樹形的數(shù)據(jù)結(jié)構(gòu)彪置,這個(gè)數(shù)據(jù)結(jié)構(gòu)我們稱為文檔對(duì)象模型拄踪。通過(guò)DOM解析器獲取Documen文檔t對(duì)象后,開發(fā)人員可以利用getDocumentElement();(獲取文檔元素)拳魁、getFirstChild()(獲取第一個(gè)子元素)等API來(lái)便捷地操作文檔樹惶桐。但DOM的缺點(diǎn)也很明顯,如果XML文檔很大就可能造成內(nèi)存溢出的風(fēng)險(xiǎn)。
2)SAX
SAX是Simple API for XML的縮寫姚糊,需要注意的是贿衍,它并不是由W3C官方所提出的標(biāo)準(zhǔn),可以說(shuō)是“民間”的事實(shí)標(biāo)準(zhǔn)救恨。實(shí)際上贸辈,它是一種社區(qū)性質(zhì)的討論產(chǎn)物,同時(shí)SAX也是在JAVA平臺(tái)上第一個(gè)被廣泛使用的XML API肠槽。雖然如此裙椭,在XML中對(duì)SAX的應(yīng)用絲毫不比DOM少,幾乎所有的XML解析器都會(huì)支持它署浩。在解析方式上揉燃,SAX是SAX是基于事件解析,不需要等到整個(gè)XML文件被加載完成后在開始處理筋栋,而是加載到哪處理到哪炊汤。這樣便帶來(lái)了效率上的優(yōu)勢(shì)。SAX處理的優(yōu)點(diǎn)非常類似于流媒體弊攘。分析能夠立即開始抢腐,而無(wú)需等待所有的數(shù)據(jù)被處理。而且襟交,由于應(yīng)用程序只是在讀取數(shù)據(jù)時(shí)檢查數(shù)據(jù)迈倍,因此不需要將數(shù)據(jù)存儲(chǔ)在內(nèi)存中。適用于大型文檔捣域。但SAX的缺點(diǎn)也很明顯:不能隨機(jī)訪問(wèn)節(jié)點(diǎn)啼染,比如我們讀取到文檔第二個(gè)元素時(shí)需要回過(guò)頭讀取第一個(gè)元素,就需要再掃描一次了焕梅。
接著說(shuō) JAXP
JAXP迹鹅,全稱Java API for XML Processing,打開其為JDK的目錄:javax.xml.parsers
贞言, 你會(huì)發(fā)現(xiàn)它與SAX和DOM一樣只是一套API斜棚。實(shí)際上,JAXP出現(xiàn)時(shí)SUN公司為了彌補(bǔ)JAVA在XML標(biāo)準(zhǔn)制定上的空白而制定的一套JAVA XML標(biāo)準(zhǔn)API该窗。它并沒(méi)有為JAVA解析XML提供任何新功能弟蚀,但是它為在JAVA獲取SAX與DOM解析器提供了更加直接的途徑。它封裝了sax\dom兩種接口酗失,并在sax\dom的基礎(chǔ)之上义钉,作了一套比較簡(jiǎn)單的api以供開發(fā)。事實(shí)上级零,我們可以把JAXP看做是sun公司對(duì)SAX和DOM二者結(jié)合后產(chǎn)生的相對(duì)來(lái)說(shuō)更加解耦的一套API断医。底層實(shí)現(xiàn)解析的方式還是DOM和SAX
我們可以將三者的區(qū)別總結(jié)成下面的表格:
雖然上面已經(jīng)介紹了DOM鉴嗤、SAX和JAXP三套接口斩启, 但是有了接口后我們并不能馬上使用,具體的實(shí)現(xiàn)還需要具體的實(shí)現(xiàn)類來(lái)完成醉锅。在java中兔簇,上述的接口都是通過(guò)xerces解釋器來(lái)實(shí)現(xiàn)的,具體在om.sun.org.apache.xerces.internal
包硬耍。xerces被稱為性能最好的解釋器垄琐,除了xerces外,還有其他的第三方解釋器经柴,如crimson狸窘。
下面我們來(lái)通過(guò)一個(gè)案例來(lái)演示java是如何通過(guò)以上三種方式解析XML文檔的:
先看xml演示文件:
<?xml version="1.0" encoding="UTF-8"?>
<school>
<teachers>
<teacher>
<name>李連杰</name>
<age>25</age>
</teacher>
<teacher>
<name>李小龍</name>
<age>65</age>
</teacher>
</teachers>
<students>
<student id="9527">
<name> 成龍< </name>
<age>14</age>
</student>
<student>
<name>小明</name>
<age>12</age>
</student>
</students>
</school>
xerces解釋器下提供了DOM解析的具體實(shí)現(xiàn)類,假設(shè)我們現(xiàn)在想要讀取所有教師的信息坯认,相關(guān)的代碼如下:
@Test
public void readXMLFile() {
DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder domBuilder = domfac.newDocumentBuilder();
File file = new File("xml文件的路徑");
InputStream is = new FileInputStream(file);
org.w3c.dom.Document doc = domBuilder.parse(is);
NodeList teacherList = doc.getElementsByTagName("teacher"); //獲取cssItem節(jié)點(diǎn)
// 定義一個(gè)List集合翻擒,承接讀取結(jié)果
List resultList = new ArrayList();
if (teacherList != null) {
for(int i=0 ; i < teacherList.getLength() ; i++) {
NodeList childNodes = teacherList.item(i).getChildNodes();
for(int j=0; j < childNodes.getLength(); j++){
if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
resultList.add(childNodes.item(j).getTextContent()); // 將結(jié)果封裝到List集合中
}
}
}
System.out.println(resultList.toString());
}
} catch (ParserConfigurationException e) {
System.out.println(e);
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (SAXException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
使用SAX方式解析,java代碼如下:
@Test
public void readXMLBySAX() throws Exception {
DefaultHandler handler = new MyDefaultHandler();
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource("xml文檔路徑"));
}
class MyDefaultHandler extends DefaultHandler{
private StringBuffer buf;
private String str;
public MyDefaultHandler(){
super();
}
public void startDocument() throws SAXException{
buf=new StringBuffer();
System.out.println("*******開始解析文檔*******");
}
public void endDocument() throws SAXException{
System.out.println("*******文檔解析結(jié)束*******");
}
public void startPrefixMapping( String prefix, String uri ){
System.out.println(" 前綴映射: " + prefix +" 開始!"+ " 它的URI是:" + uri);
}
public void endPrefixMapping( String prefix ){
System.out.println(" 前綴映射: "+prefix+" 結(jié)束!");
}
public void startElement(String namespaceURI,String localName,String qName,Attributes atts){
System.out.println("*******開始解析元素*******");
System.out.println("元素名"+qName);
for(int i=0;i<atts.getLength();i++){
System.out.println("元素名"+atts.getLocalName(i)+"屬性值"+atts.getValue(i));
}
}
public void endElement(String namespaceURI,String localName,String fullName )throws SAXException{
str = buf.toString();
System.out.println("str = "+str.trim()+" || length = "+str.trim().length());
buf.delete(0,buf.length());
System.out.println("******"+namespaceURI+"元素解析結(jié)束"+localName+"********"+fullName);
}
public void characters( char[] chars, int start, int length )throws SAXException{
//將元素內(nèi)容累加到StringBuffer中
buf.append(chars,start,length);
}
}
使用JAXP的DOM相關(guān)API:
/**
* 由于JAXP封裝了DOM和SAX接口牛哺,所以兩種解析方式都可以陋气。這里的話只舉例DOM
*/
@Test
public void readXMLByJaxp() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.parse("xml路徑");
NodeList teacherList = document.getElementsByTagName("teacher");
List resultList = new ArrayList();
if (teacherList != null) {
for(int i=0 ; i < teacherList.getLength() ; i++) {
NodeList childNodes = teacherList.item(i).getChildNodes();
for(int j=0; j < childNodes.getLength(); j++){
if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
resultList.add(childNodes.item(j).getTextContent()); // 將結(jié)果封裝到List集合中
}
}
}
System.out.println(resultList.toString());
}
}
我們可以發(fā)現(xiàn)使用JAXP解析XML文檔和分別使用其他兩種方式解析是差不多的。同時(shí)引润,解釋器解析文檔的步驟還是略顯麻煩巩趁,尤其是SAX解析,特別麻煩淳附。由于Java原生的API使用起來(lái)并不方便议慰,所以也就引起了后面更加靈活、更加簡(jiǎn)便的JDOM和DOM4J出現(xiàn)燃观。
1)JDOM
JDOM是專門為java提出的褒脯,使用效率比普通DOM更快。由于是第一個(gè) Java 特定模型缆毁,JDOM 一直得到大力推廣和促進(jìn)。正在考慮通過(guò)“Java 規(guī)范請(qǐng)求 JSR-102”將它最終用作“Java 標(biāo)準(zhǔn)擴(kuò)展”到涂。從 2000 年初就已經(jīng)開始了 JDOM 開發(fā)脊框。JDOM 與 DOM 主要有兩方面不同。1践啄、JDOM 僅使用具體類而不使用接口 2浇雹、JDOM中大量使用了Collections類,簡(jiǎn)化了開發(fā)人員的上手難度屿讽。
2)DOM4J
DOM4J 代表了完全獨(dú)立的開發(fā)結(jié)果昭灵,但最初吠裆,它是 JDOM 的一種智能分支。它合并了許多超出基本 XML 文檔表示的功能烂完,包括集成的 XPath 支持试疙、XML Schema 支持以及用于大文檔或流化文檔的基于事件的處理。它還提供了構(gòu)建文檔表示的選項(xiàng)抠蚣,它通過(guò) DOM4J API 和標(biāo)準(zhǔn) DOM 接口具有并行訪問(wèn)功能祝旷。從 2000 下半年開始,它就一直處于開發(fā)之中嘶窄。DOM4J
有性能優(yōu)異怀跛、功能強(qiáng)大和極端易用使用的特點(diǎn),同時(shí)它也是一個(gè)開放源代碼的軟件柄冲。如今你可以看到越來(lái)越多的 Java 軟件都在使用 DOM4J 來(lái)讀寫XML
吻谋,特別值得一提的是連Sun
的JAXM
也在用DOM4J
。
目前來(lái)說(shuō)现横,這兩種手段都有項(xiàng)目還在使用漓拾,但DOM4J已經(jīng)成為了各種框架和項(xiàng)目使用的主流,例如大名鼎鼎的 hibernate也用 DOM4J 來(lái)讀取 XML 配置文件长赞。下面我們就用DOM4J來(lái)做一個(gè)解析小案例吧晦攒。
解析并輸出xml文檔的老師姓名和年齡(xml文件和之前的用例一致)
@Test
public void parseXmlByDom4j() throws DocumentException {
// 1、 創(chuàng)建SAXReader對(duì)象
SAXReader reader = new SAXReader();
// 2得哆、指定要解析的XML文件
Document document = reader.read("xml的路徑").getDocument();
// 3脯颜、 遍歷所有teacer節(jié)點(diǎn)下的內(nèi)容
Element rootElement = document.getRootElement();
for (Element element : rootElement.element("teachers").elements()) {
String qname = element.getQName().getName();
String teacherName = element.element("name").getText();
String teacherAge = element.element("age").getText();
System.out.println(qname + ": name = " + teacherName + " age = " + teacherAge);
}
}
最后說(shuō)說(shuō)JAXB
JAXB(Java Architecture for XML Binding) 是一個(gè)業(yè)界的標(biāo)準(zhǔn),是一項(xiàng)可以根據(jù)XML Schema產(chǎn)生Java類的技術(shù)贩据。該過(guò)程中栋操,JAXB也提供了將XML實(shí)例文檔反向生成Java對(duì)象樹的方法,并能將Java對(duì)象樹的內(nèi)容重新寫到XML實(shí)例文檔饱亮。從另一方面來(lái)講矾芙,JAXB提供了快速而簡(jiǎn)便的方法將XML模式綁定到Java表示,從而使得Java開發(fā)者在Java應(yīng)用程序中能方便地結(jié)合XML數(shù)據(jù)和處理函數(shù)近上。
簡(jiǎn)單來(lái)說(shuō)就是剔宪,JAXB為我們提供了這樣一種技術(shù):可以通過(guò)schema直接將xml文檔轉(zhuǎn)為JAVA對(duì)象。這種技術(shù)是在JDK1.6之后開始應(yīng)用起來(lái)的壹无,在一些業(yè)務(wù)場(chǎng)景下使用會(huì)更加方便葱绒。
小結(jié)
本篇文章從解析XML的兩種方式DOM和SAX開始講起,引出了Sun公司為了補(bǔ)充自身在xml解析上的空白而推出的JAXP解析(只是將DOM和SAX進(jìn)行簡(jiǎn)單封裝)斗锭,又因?yàn)镴AXP本身并沒(méi)有解決API本身的復(fù)雜性地淀,所以后面市場(chǎng)上就又出現(xiàn)了JDOM和DOM4J解析,他們專門為JAVA設(shè)計(jì)岖是,簡(jiǎn)便實(shí)用帮毁,其中又以功能強(qiáng)大的DOM4J成為了市場(chǎng)使用的主流实苞。同時(shí)在JDK1.6之后,java也推出了JAXB簡(jiǎn)化了一些場(chǎng)景下對(duì)XML解析的使用烈疚。
至此黔牵,本篇文章對(duì)于java解析XML的歷史已經(jīng)介紹完畢,希望對(duì)各位讀者有所收獲胞得。對(duì)于JAXM荧止、JAX-RPC等概念由于日常工作中我們很少會(huì)接觸到,就不在本篇文章中介紹了阶剑。同時(shí)跃巡,本篇文章也參考了多篇文章的內(nèi)容講解,大家也可以從參考資料中再去看其他更加詳細(xì)的內(nèi)容牧愁。
參考資料
Java for XML: JAXP素邪、JAXB、JAXM猪半、JAX-RPC兔朦、JAX-WS:
https://blog.csdn.net/hotdust/article/details/52016804
DOM、JDOM磨确、DOM4J的區(qū)別:
https://www.cnblogs.com/avivahe/p/5493060.html
關(guān)于SAX沽甥,DOM,JAXP乏奥,JDOM摆舟,DOM4J的一些理解:
https://blog.csdn.net/robertleejesus/article/details/2624434
dom4j的使用和分析(重點(diǎn)對(duì)比和DOM、SAX的區(qū)別):
https://zhuanlan.zhihu.com/p/106094171
用DefaultHandler解析XML的Demo:
https://blog.csdn.net/feishin/article/details/8610272