XML系列(下篇)——Java和XML的這些事你可能真的不知道

前言

我們?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

DOMSAX都是對(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é)成下面的表格:


DOM滞乙、SAX、JAXP三者對(duì)比

雖然上面已經(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> 成龍&lt; </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吻谋,特別值得一提的是連SunJAXM也在用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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末邓了,一起剝皮案震驚了整個(gè)濱河市恨诱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骗炉,老刑警劉巖照宝,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異句葵,居然都是意外死亡厕鹃,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門乍丈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)熊响,“玉大人,你說(shuō)我怎么就攤上這事诗赌。” “怎么了秸弛?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵铭若,是天一觀的道長(zhǎng)洪碳。 經(jīng)常有香客問(wèn)我,道長(zhǎng)叼屠,這世上最難降的妖魔是什么瞳腌? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮镜雨,結(jié)果婚禮上嫂侍,老公的妹妹穿的比我還像新娘。我一直安慰自己荚坞,他們只是感情好挑宠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颓影,像睡著了一般各淀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诡挂,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天碎浇,我揣著相機(jī)與錄音,去河邊找鬼璃俗。 笑死奴璃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的城豁。 我是一名探鬼主播苟穆,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钮蛛!你這毒婦竟也來(lái)了鞭缭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤魏颓,失蹤者是張志新(化名)和其女友劉穎岭辣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甸饱,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沦童,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹话。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷遗。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驼壶,靈堂內(nèi)的尸體忽然破棺而出氏豌,到底是詐尸還是另有隱情,我是刑警寧澤热凹,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布泵喘,位于F島的核電站泪电,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纪铺。R本人自食惡果不足惜相速,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鲜锚。 院中可真熱鬧突诬,春花似錦、人聲如沸芜繁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浆洗。三九已至催束,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伏社,已是汗流浹背抠刺。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摘昌,地道東北人速妖。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像聪黎,于是被迫代替她去往敵國(guó)和親罕容。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容