XML就是這么簡單

什么是XML库物?

XML:extensiable markup language 被稱作可擴展標記語言

XML簡單的歷史介紹:

  • gml->sgml->html->xml
  • gml(通用標記語言)–在不同的機器進行通信的數(shù)據(jù)規(guī)范
  • sgml(標準通用標記語言)
  • html(超文本標記語言)

為什么我們需要使用XML呢?

  • ①我們沒有XML這種語言之前,我們使用的是String作為兩個程序之間的通訊育苟!現(xiàn)在問題就來了,如果我們傳輸?shù)氖菐в嘘P系型結構的數(shù)據(jù)边灭,String怎么表達呢丘跌?String對關系型數(shù)據(jù)不擅長,要是描述起來也難免會有歧義的時候狠角!關系型數(shù)據(jù)如圖下所示:
image
  • HTML語言本身就有缺陷
    • 標記都是固定的号杠,不能自定義。HTML語言中有什么標記就只能用什么標記
    • HTML標簽本身就缺少含義(tr標簽里面什么內(nèi)容都能放進去丰歌,不規(guī)范姨蟋!!)
    • HTML沒有實現(xiàn)真正的國際化

XML文件就解決了以上的問題了,如果使用XML描述上述圖片的關系立帖,是非常簡單的眼溶!


    <?xml version="1.0" encoding="UTF-8" ?>
    <中國>
        <北京>
            <海淀></海淀>
            <豐臺></豐臺>
        </北京>
        <湖南>
            <長沙></長沙>
            <岳陽></岳陽>
        </湖南>
        <湖北>
            <武漢></武漢>
            <荊州></荊州>
        </湖北>
    </中國>

XML文件還能使用瀏覽器打開:

image

我們可以發(fā)現(xiàn)XML是可以描述很復雜的數(shù)據(jù)關系的


XML的用途

①:配置文件(例子:Tomcat的web.xml,server.xml......),XML能夠非常清晰描述出程序之間的關系

②:程序間數(shù)據(jù)的傳輸晓勇,XML的格式是通用的偷仿,能夠減少交換數(shù)據(jù)時的復雜性!

③:充當小型數(shù)據(jù)庫宵蕉,如果我們的數(shù)據(jù)有時候需要人工配置的酝静,那么XML充當小型的數(shù)據(jù)庫是個不錯的選擇,程序直接讀取XML文件顯然要比讀取數(shù)據(jù)庫要快呢!


XML的技術架構

XML被設計為“什么都不做”,XML數(shù)據(jù)或XML文檔只用于組織冈闭、存儲數(shù)據(jù),除此之外的數(shù)據(jù)生成薄榛、讀取、傳送让歼、存取等等操作都與XML本身無關敞恋!

于是乎,想要操作XML谋右,就需要用到XML之外的技術了

  • 為XML定規(guī)則:現(xiàn)在一般使用DTD或Schema技術硬猫,當然了Schema技術更為先進!
  • 解析XML的數(shù)據(jù):一般使用DOM或者SAX技術,各有各的優(yōu)點
  • 提供樣式:XML一般用來存儲數(shù)據(jù)的啸蜜,但設計者野心很大坑雅,也想用來顯示數(shù)據(jù)(但沒人用XML來顯示數(shù)據(jù)),就有了XSLT(eXtensiable Stylesheet Language Transformation)可擴展樣式轉換語言

XML語法:

文檔聲明:

  • XML聲明放在XML的第一行

  • version----版本

  • encoding--編碼

  • standalone--獨立使用--默認是no衬横。standalone表示該xml是不是獨立的裹粤,如果是yes,則表示這個XML文檔時獨立的蜂林,不能引用外部的DTD規(guī)范文件遥诉;如果是no,則該XML文檔不是獨立的噪叙,表示可以引用外部的DTD規(guī)范文檔矮锈。

  • 正確的文檔聲明格式,屬性的位置不能改變构眯!


    <?xml version="1.0" encoding="utf-8" standalone="no"?>

元素

首先在這里說明一個概念:在XML中元素和標簽指的是同一個東西愕难!不要被不同的名稱所迷惑了早龟!

元素中需要值得注意的地方

  • XML元素中的出現(xiàn)的空格和換行都會被當做元素內(nèi)容進行處理
  • 每個XML文檔必須有且只有一個根元素
  • 元素必須閉合
  • 大小寫敏感
  • 不能交叉嵌套
  • 不能以數(shù)字開頭

看起來好像有很多需要值得注意的地方惫霸,其實只需要記住:XML的語法是規(guī)范的葱弟!不要隨意亂寫壹店!


屬性

屬性是作為XML元素中的一部分的,命名規(guī)范也是和XML元素一樣的芝加!


    <!--屬性名是name硅卢,屬性值是china-->
    <中國 name="china">

    </中國>


注釋

注釋和HTML的注釋是一樣的


    <!---->

CDATA

在編寫XML文件時,有些內(nèi)容可能不想讓解析引擎解析執(zhí)行藏杖,而是當作原始內(nèi)容處理将塑。遇到此種情況,可以把這些內(nèi)容放在CDATA區(qū)里蝌麸,對于CDATA區(qū)域內(nèi)的內(nèi)容点寥,XML解析程序不會處理,而是直接原封不動的輸出

語法:


    <![CDATA[
        ...內(nèi)容
    ]]>

轉義字符

對于一些單個字符来吩,若想顯示其原始樣式敢辩,也可以使用轉義的形式予以處理。

image

處理指令

處理指令弟疆,簡稱PI (processing instruction)戚长。處理指令用來指揮解析引擎如何解析XML文檔內(nèi)容。

例如:

在XML文檔中可以使用xml-stylesheet指令怠苔,通知XML解析引擎同廉,應用css文件顯示xml文檔內(nèi)容。

    <?xml-stylesheet type="text/css" href="1.css"?>
  • XML代碼:

    <?xml version="1.0" encoding="UTF-8" ?>
    <?xml-stylesheet type="text/css" href="1.css"?>

    <china>
        <guangzhou>
            廣州
        </guangzhou>
        <shenzhen>
            深圳
        </shenzhen>
    </china>

  • CSS代碼:

    guangzhou{
        font-size: 40px;
    }
  • 效果:
image

JDK中的XML API

①:JAXP(The Java API For XML Processing):主要負責解析XML

②:JAXB(Java Architecture for XML Binding):主要負責將XML映射為Java對象

什么是XML解析

前面XML章節(jié)已經(jīng)說了,XML被設計為“什么都不做”恤溶,XML只用于組織乓诽、存儲數(shù)據(jù),除此之外的數(shù)據(jù)生成咒程、讀取鸠天、傳送等等的操作都與XML本身無關!

XML解析就是讀取XML的數(shù)據(jù)帐姻!


XML解析方式

XML解析方式分為兩種:

①:dom(Document Object Model)文檔對象模型稠集,是W3C組織推薦解析XML的一種方式

②:sax(Simple API For XML),它是XML社區(qū)的標準饥瓷,幾乎所有XML解析器都支持它剥纷!

XML解析操作

image

從上面的圖很容易發(fā)現(xiàn),應用程序不是直接對XML文檔進行操作的呢铆,而是由XML解析器對XML文檔進行分析晦鞋,然后應用程序通過XML解析器所提供的DOM接口或者SAX接口對分析結果進行操作,從而間接地實現(xiàn)了對XML文檔的訪問棺克!

常用的解析器和解析開發(fā)包的關系如下所示

image

為什么有3種開發(fā)包悠垛?

  • jaxp開發(fā)包是JDK自帶的,不需要導入開發(fā)包娜谊。
  • 由于sun公司的jaxp不夠完善确买,于是就被研發(fā)了Jdom。XML解析如果使用Jdom纱皆,需要導入開發(fā)包
  • dom4j是由于Jdom的開發(fā)人員出現(xiàn)了分歧湾趾,dom4j由Jdom的一批開發(fā)人員所研發(fā)。XML解析如果使用Jdom派草,需要導入開發(fā)包【現(xiàn)在用dom4j是最多的搀缠!】

jaxp

雖然jaxp解析XML的性能以及開發(fā)的簡易度是沒有dom4j好,但是jaxp不管怎么說都是JDK內(nèi)置的開發(fā)包近迁,我們是需要學習的艺普!

DOM解析操作

DOM解析是一個基于對象的API,它把XML的內(nèi)容加載到內(nèi)存中钳踊,生成與XML文檔內(nèi)容對應的模型衷敌!當解析完成,內(nèi)存中會生成與XML文檔的結構與之對應的DOM對象樹拓瞪,這樣就能夠根據(jù)樹的結構缴罗,以節(jié)點的形式對文檔進行操作!

簡單來說:DOM解析會把XML文檔加載到內(nèi)存中祭埂,生成DOM樹的元素都是以對象的形式存在的面氓!我們操作這些對象就能夠操作XML文檔了兵钮!

  • 下面這樣圖就能很好地說明了,是怎么樣生成與XML文檔內(nèi)容對應的DOM樹舌界!
image

既然XML文檔的數(shù)據(jù)是帶有關系型的掘譬,那么生成的DOM樹的節(jié)點也是有關系的:

  • 位于一個節(jié)點之上的節(jié)點是該節(jié)點的父節(jié)點(parent)
  • 一個節(jié)點之下的節(jié)點是該節(jié)點的子節(jié)點(children)
  • 同一層次,具有相同父節(jié)點的節(jié)點是兄弟節(jié)點(sibling)
  • 一個節(jié)點的下一個層次的節(jié)點集合是節(jié)點后代(descendant)
  • 父呻拌、祖父節(jié)點及所有位于節(jié)點上面的葱轩,都是節(jié)點的祖先(ancestor)

在DOM解析中有幾個核心的操作接口:

  • Document【代表整個XML文檔,通過Document節(jié)點可以訪問XML文件中所有的元素內(nèi)容藐握!】
  • Node【Node節(jié)點幾乎在XML操作接口中幾乎相當于普通Java類的Object靴拱,很多核心接口都實現(xiàn)了它,在下面的關系圖可以看出猾普!】
  • NodeList【代表著一個節(jié)點的集合袜炕,通常是一個節(jié)點中子節(jié)點的集合!】
  • NameNodeMap【表示一組節(jié)點和其唯一名稱對應的一一對應關系初家,主要用于屬性節(jié)點的表示(書上說是核心的操作接口偎窘,但我好像沒用到!呃呃呃溜在,等我用到了陌知,我再來填坑!)】

節(jié)點之間的關系圖:

image
  • 有人可能會很難理解炕泳,為什么Document接口比Node接口還小纵诞,呃呃呃上祈,我是這樣想的:一個Document由無數(shù)個Node組成培遵,這樣我也能把Document當成是Node呀!如果實在想不通:人家都這樣設計了登刺,你有種就不用白淹蟆!纸俭!(開玩笑的.....)

好的皇耗,不跟你們多bb,我們來使用一下Dom的方式解析XML文檔吧揍很!

  • XML文檔代碼

    <?xml version="1.0" encoding="UTF-8" ?>
    <china>
        <guangzhou >廣州</guangzhou>
        <shenzhen>深圳</shenzhen>
        <beijing>北京</beijing>
        <shanghai>上海</shanghai>
    </china>

  • 根據(jù)XML解析的流程圖郎楼,我們先要獲取到解析器對象!

    public class DomParse {

        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {

            //API規(guī)范:需要用一個工廠來造解析器對象窒悔,于是我先造了一個工廠呜袁!
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            //獲取解析器對象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            //獲取到解析XML文檔的流對象
            InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");

            //解析XML文檔,得到了代表XML文檔的Document對象简珠!
            Document document = documentBuilder.parse(inputStream);

        }
    }
  • 解析XML文檔的內(nèi)容用來干嘛阶界?無非就是增刪改查遍歷,只要我們會對XML進行增刪改查,那就說明我們是會使用DOM解析的膘融!

遍歷

  • 我們再來看一下XML文檔的內(nèi)容芙粱,如果我們要遍歷該怎么做?
image
  • 可能我們會有兩種想法
    • ①:從XML文檔內(nèi)容的上往下看氧映,看到什么就輸出什么春畔!【這正是SAX解析的做法】
    • ②:把XML文檔的內(nèi)容分成兩部分,一部分是有子節(jié)點的岛都,一部分是沒有子節(jié)點的(也就是元素節(jié)點9涨ā)。首先我們判斷是否為元素節(jié)點疗绣,如果是元素節(jié)點就輸出线召,不是元素節(jié)點就獲取到子節(jié)點的集合,再判斷子節(jié)點集合中的是否是元素節(jié)點多矮,如果是元素節(jié)點就輸出缓淹,如果不是元素節(jié)點獲取到該子節(jié)點的集合....好的,一不小心就遞歸了...
  • 我們來對XML文檔遍歷一下吧塔逃,為了更好地重用讯壶,就將它寫成一個方法吧(也是能夠更好地用遞歸實現(xiàn)功能)

    public class DomParse {

        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {

            //API規(guī)范:需要用一個工廠來造解析器對象湾盗,于是我先造了一個工廠伏蚊!
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            //獲取解析器對象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            //獲取到解析XML文檔的File對象
            InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");

            //解析XML文檔,得到了代表XML文檔的Document對象格粪!
            Document document = documentBuilder.parse(inputStream);

            //把代表XML文檔的document對象傳遞進去給list方法
            list(document);

        }

        //我們這里就接收Node類型的實例對象吧躏吊!多態(tài)!U饰比伏!
        private static void list(Node node) {

            //判斷是否是元素節(jié)點,如果是元素節(jié)點就直接輸出
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                System.out.println(node.getNodeName());
            }

            //....如果沒有進入if語句疆导,下面的肯定就不是元素節(jié)點了赁项,所以獲取到子節(jié)點集合
            NodeList nodeList = node.getChildNodes();

            //遍歷子節(jié)點集合
            for (int i = 0; i < nodeList.getLength(); i++) {

                //獲取到其中的一個子節(jié)點
                Node child = nodeList.item(i);

                //...判斷該子節(jié)點是否為元素節(jié)點,如果是元素節(jié)點就輸出澈段,不是元素節(jié)點就再獲取到它的子節(jié)點集合...遞歸了

                list(child);
            }

        }
    }

  • 效果:
image

查詢

現(xiàn)在我要做的就是:讀取guangzhou這個節(jié)點的文本內(nèi)容悠菜!


    private static void read(Document document) {

        //獲取到所有名稱為guangzhou節(jié)點
        NodeList nodeList = document.getElementsByTagName("guangzhou");

        //取出第一個名稱為guangzhou的節(jié)點
        Node node = nodeList.item(0);

        //獲取到節(jié)點的文本內(nèi)容
        String value = node.getTextContent();

        System.out.println(value);

    }
  • 效果:
image

增加

現(xiàn)在我想多增加一個城市節(jié)點(杭州),我需要這樣做:


    private static void add(Document document) {

        //創(chuàng)建需要增加的節(jié)點
        Element element = document.createElement("hangzhou");

        //向節(jié)點添加文本內(nèi)容
        element.setTextContent("杭州");

        //得到需要添加節(jié)點的父節(jié)點
        Node parent = document.getElementsByTagName("china").item(0);

        //把需要增加的節(jié)點掛在父節(jié)點下面去
        parent.appendChild(element);

    }
  • 做到這里败富,我僅僅在內(nèi)存的Dom樹下添加了一個節(jié)點悔醋,要想把內(nèi)存中的Dom樹寫到硬盤文件中,需要轉換器囤耳!

  • 獲取轉換器也十分簡單


        //獲取一個轉換器它需要工廠來造篙顺,那么我就造一個工廠
        TransformerFactory transformerFactory = TransformerFactory.newInstance();

        //獲取轉換器對象
        Transformer transformer = transformerFactory.newTransformer();
  • 把內(nèi)存中的Dom樹更新到硬盤文件中的transform()方法就稍稍有些復雜了偶芍!
image
  • 它需要一個Source實例對象和Result的實例對象,這兩個接口到底是什么玩意暗旅怠匪蟀?

  • 于是乎,我就去查API宰僧,發(fā)現(xiàn)DomSource實現(xiàn)了Source接口材彪,我們使用的不正是Dom解析嗎,再看看構造方法琴儿,感覺就是它了段化!

image
  • 而SteamResult實現(xiàn)了Result接口,有人也會想造成,DomResult也實現(xiàn)了Result接口啊显熏,為什么不用DomResult呢?我們現(xiàn)在做的是把內(nèi)存中的Dom樹更新到硬盤文件中呀晒屎,當然用的是StreamResult啦喘蟆!

  • 完整代碼如下:


    private static void add(Document document) throws TransformerException {

        //創(chuàng)建需要增加的節(jié)點
        Element element = document.createElement("hangzhou");

        //向節(jié)點添加文本內(nèi)容
        element.setTextContent("杭州");

        //得到需要添加節(jié)點的父節(jié)點
        Node parent = document.getElementsByTagName("china").item(0);

        //把需要增加的節(jié)點掛在父節(jié)點下面去
        parent.appendChild(element);

        //獲取一個轉換器它需要工廠來造,那么我就造一個工廠
        TransformerFactory transformerFactory = TransformerFactory.newInstance();

        //獲取轉換器對象
        Transformer transformer = transformerFactory.newTransformer();

        //把內(nèi)存中的Dom樹更新到硬盤中
        transformer.transform(new DOMSource(document),new StreamResult("city.xml"));
    }
  • 效果:
image

剛剛增加的節(jié)點是在china節(jié)點的末尾處的鼓鲁,現(xiàn)在我想指定增加節(jié)點的在beijing節(jié)點之前蕴轨,是這樣做的:


    private static void add2(Document document) throws TransformerException {

        //獲取到beijing節(jié)點
        Node beijing = document.getElementsByTagName("beijing").item(0);

        //創(chuàng)建新的節(jié)點
        Element element = document.createElement("guangxi");

        //設置節(jié)點的文本內(nèi)容
        element.setTextContent("廣西");

        //獲取到要創(chuàng)建節(jié)點的父節(jié)點,
        Node parent = document.getElementsByTagName("china").item(0);

        //將guangxi節(jié)點插入到beijing節(jié)點之前骇吭!
        parent.insertBefore(element, beijing);

        //將內(nèi)存中的Dom樹更新到硬盤文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));

    }
  • 效果:
image

刪除

現(xiàn)在我要刪除的是beijing這個節(jié)點橙弱!


    private static void delete(Document document) throws TransformerException {

        //獲取到beijing這個節(jié)點
        Node node = document.getElementsByTagName("beijing").item(0);

        //獲取到父節(jié)點,然后通過父節(jié)點把自己刪除了
        node.getParentNode().removeChild(node);

        //把內(nèi)存中的Dom樹更新到硬盤文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));

    }
  • 效果:
image

修改

將guangzhou節(jié)點的文本內(nèi)容修改成廣州你好


    private static void update(Document document) throws TransformerException {

        //獲取到guangzhou節(jié)點
        Node node = document.getElementsByTagName("guangzhou").item(0);

        node.setTextContent("廣州你好");

        //將內(nèi)存中的Dom樹更新到硬盤文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));

    }

  • 效果:
image

操作屬性

XML文檔是可能帶有屬性值的燥狰,現(xiàn)在我們要guangzhou節(jié)點上的屬性

    private static void updateAttribute(Document document) throws TransformerException {

        //獲取到guangzhou節(jié)點
        Node node = document.getElementsByTagName("guangzhou").item(0);

        //現(xiàn)在node節(jié)點沒有增加屬性的方法棘脐,所以我就要找它的子類---Element
        Element guangzhou = (Element) node;

        //設置一個屬性,如果存在則修改碾局,不存在則創(chuàng)建荆残!
        guangzhou.setAttribute("play", "gzchanglong");

        //如果要刪除屬性就用removeAttribute()方法

        //將內(nèi)存中的Dom樹更新到硬盤文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));

    }
  • 效果:
image

SAX解析

SAX采用的是一種順序的模式進行訪問奴艾,是一種快速讀取XML數(shù)據(jù)的方式净当。當時候SAX解析器進行操作時,會觸發(fā)一系列事件SAX蕴潦。采用事件處理的方式解析XML文件像啼,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器

sax是一種推式的機制,你創(chuàng)建一個sax 解析器,解析器在發(fā)現(xiàn)xml文檔中的內(nèi)容時就告訴你(把事件推給你). 如何處理這些內(nèi)容潭苞,由程序員自己決定忽冻。

當解析器解析到<?xml version="1.0" encoding="UTF-8" standalone="no"?>聲明頭時,會觸發(fā)事件此疹。解析到<china>元素頭時也會觸發(fā)事件僧诚!也就是說:當使用SAX解析器掃描XML文檔(也就是Document對象)開始遮婶、結束,以及元素的開始湖笨、結束時都會觸發(fā)事件旗扑,根據(jù)不同事件調用相對應的方法!

image

首先我們還是先拿到SAX的解析器再說吧!


        //要得到解析器對象就需要造一個工廠,于是我造了一個工廠
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

        //獲取到解析器對象
        SAXParser saxParse = saxParserFactory.newSAXParser();

  • 調用解析對象的解析方法的時候慈省,需要的不僅僅是XML文檔的路徑臀防!還需要一個事件處理器!
image
  • 事件處理器都是由我們程序員來編寫的边败,它一般繼承DefaultHandler類袱衷,重寫如下5個方法:

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
    }
  • 獲取解析器,調用解析器解析XML文檔的代碼:

    public static void main(String[] args) throws Exception{

        //要得到解析器對象就需要造一個工廠笑窜,于是我造了一個工廠
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

        //獲取到解析器對象
        SAXParser saxParse = saxParserFactory.newSAXParser();

        //獲取到XML文檔的流對象
        InputStream inputStream = SAXParse.class.getClassLoader().getResourceAsStream("city.xml");

        saxParse.parse(inputStream, new MyHandler());

    }
  • 事件處理器的代碼:

    public class MyHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            System.out.println("我開始來掃描啦V略铩!E沤亍篡悟!");
        }

        @Override
        public void endDocument() throws SAXException {

            System.out.println("我結束了!X仪蕖0嵩帷!");
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

            //如果要解析出節(jié)點屬性的內(nèi)容艳悔,也非常簡單急凰,只要通過attributes變量就行了!

            //輸出節(jié)點的名字猜年!
            System.out.println(qName);
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {

            System.out.println(qName);
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {

            System.out.println(new String(ch,start,length));
        }
    }
  • 我們發(fā)現(xiàn)抡锈,事件處理器的代碼都非常簡單,然后就如此簡單地就能夠遍歷整個XML文檔了乔外!

  • 如果要查詢單獨的某個節(jié)點的內(nèi)容也是非常簡單的喲床三!只要在startElement()方法中判斷名字是否相同即可!

  • 現(xiàn)在我只想查詢guangzhou節(jié)點的內(nèi)容:


    //定義一個標識量杨幼,用于指定查詢某個節(jié)點的內(nèi)容
    boolean flag = false;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        //如果節(jié)點名稱是guangzhou撇簿,我才輸出,并且把標識量設置為true
        if (qName == "guangzhou") {
            System.out.println(qName);
            flag = true;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        //只有在flag為true的情況下我才輸出文本的內(nèi)容
        if (flag == true) {
            System.out.println(new String(ch, start, length));

        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {

        //在執(zhí)行到元素的末尾時差购,不要忘了將標識量改成false
        if (qName == "guangzhou" && flag == true) {
            System.out.println(qName);
            flag = false;

        }
    }
  • 效果:
image

DOM和SAX解析的區(qū)別:

DOM解析讀取整個XML文檔四瘫,在內(nèi)存中形成DOM樹,很方便地對XML文檔的內(nèi)容進行增刪改欲逃。但如果XML文檔的內(nèi)容過大找蜜,那么就會導致內(nèi)存溢出!

SAX解析采用部分讀取的方式稳析,可以處理大型文件洗做,但只能對文件按順序從頭到尾解析一遍弓叛,不支持文件的增刪改操作

DOM和SAX解析有著明顯的差別,什么時候使用DOM或者SAX就非常明了了诚纸。


dom4j

Dom4j是一個非常優(yōu)秀的Java XML API邪码,具有性能優(yōu)異、功能強大和極易使用的特點咬清。

為什么需要有dom4j

  • dom缺點:比較耗費內(nèi)存

  • sax缺點:只能對xml文件進行讀取,不能修改,添加,刪除

  • dom4j:既可以提高效率,同時也可以進行crud操作

因為dom4j不是sun公司的產(chǎn)品,所以我們開發(fā)dom4j需要導入開發(fā)包


獲取dom4j的解析器

  • 使用dom4j對XML文檔進行增刪改查闭专,都需要獲取到dom4j的解析器

        //獲取到解析器
        SAXReader saxReader = new SAXReader();

        //獲取到XML文件的流對象
        InputStream inputStream = DOM4j.class.getClassLoader().getResourceAsStream("1.xml");

        //通過解析器讀取XML文件
        Document document = saxReader.read(inputStream);

獲取Document對象

我們都知道,Document代表的是XML文檔旧烧,一般我們都是通過Document對象開始影钉,來進行CRUD(增刪改查)操作的!

獲取Document對象有三種方式:

①:讀取XML文件,獲得document對象(這種最常用)

SAXReader reader = new SAXReader()掘剪;
Document document = reader.read(new File("input.xml"));

②:解析XML形式的文本,得到document對象


    String text = "<members></members>";
    Document document=DocumentHelper.parseText(text);

③:主動創(chuàng)建document對象.


Document document =DocumentHelper.createDocument();

//創(chuàng)建根節(jié)點
Element root = document.addElement("members");

CRUD的重要一句話:

讀取XML文檔的數(shù)據(jù)平委,都是通過Document獲取根元素,再通過根元素獲取得到其他節(jié)點的夺谁,從而進行操作廉赔!

如果XML的結構有多層,需要一層一層地獲蓉遗浮蜡塌!

查詢


    @Test
    public void read() throws DocumentException {

        //獲取到解析器
        SAXReader saxReader = new SAXReader();

        //獲取到XML文件的流對象
        InputStream inputStream = dom4j11.class.getClassLoader().getResourceAsStream("1.xml");

        //通過解析器讀取XML文件
        Document document = saxReader.read(inputStream);

        //獲取得到根節(jié)點
        Element root = document.getRootElement();

        //獲取得到name節(jié)點
        Element name = root.element("name");

        //得到了name節(jié)點,就可以獲取name節(jié)點的屬性或者文本內(nèi)容了勿负!
        String text = name.getText();

        String attribute = name.attributeValue("littleName");

        System.out.println("文本內(nèi)容是:" + text);
        System.out.println("屬性內(nèi)容是:" + attribute);

    }

  • XML文件如下:

    <?xml version="1.0" encoding="UTF-8" ?>
         <person>
        <name littleName="fucheng">zhongfucheng</name>
        <age>20</age>
    </person>

  • 效果:
image
  • 多層結構的查詢:

        //獲取得到根節(jié)點
        Element root = document.getRootElement();

        //一層一層地獲取到節(jié)點
        Element element = root.element("guangdong").element("guangzhou").element("luogang");

        String value = element.getText();

        System.out.println(value);
  • XML文件和結果:
image

增加

在DOM4j中要對內(nèi)存中的DOM樹寫到硬盤文件中馏艾,也是要有轉換器的支持的!

dom4j提供了XMLWriter供我們對XML文檔進行更新操作奴愉,一般地創(chuàng)建XMLWriter的時候我們都會給出兩個參數(shù)琅摩,一個是Writer,一個是OutputFormat

image

這個OutputFormat有什么用的呢锭硼?其實就是指定回寫XML的格式和編碼格式房资。細心的朋友會發(fā)現(xiàn),上面我們在jaxp包下使用dom解析的Transformer類檀头,把內(nèi)存中的DOM樹更新到文件硬盤中轰异,是沒有格式的!不信倒回去看看鳖擒!這個OutputFormat就可以讓我們更新XML文檔時也能帶有格式溉浙!


        //創(chuàng)建帶有格式的對象
        OutputFormat outputFormat = OutputFormat.createPrettyPrint();

        //設置編碼,默認的編碼是gb2312蒋荚,讀寫的編碼不一致,會導致亂碼的馆蠕!
        outputFormat.setEncoding("UTF-8");

        //創(chuàng)建XMLWriter對象
        XMLWriter xmlWriter = new XMLWriter(new FileWriter("2.xml"), outputFormat);

        //XMLWriter對象寫入的是document
        xmlWriter.write(document);

        //關閉流
        xmlWriter.close();
  • 下面我們就為在person節(jié)點下新創(chuàng)建一個name節(jié)點吧,完整的代碼如下:期升!

    @Test
    public void add() throws Exception {

        //獲取到解析器
        SAXReader saxReader = new SAXReader();

        //獲取到XML文件的流對象
        InputStream inputStream = dom4j11.class.getClassLoader().getResourceAsStream("1.xml");

        //通過解析器讀取XML文件
        Document document = saxReader.read(inputStream);

        //創(chuàng)建出新的節(jié)點惊奇,為節(jié)點設置文本內(nèi)容
        Element newElement = DocumentHelper.createElement("name");
        newElement.setText("ouzicheng");

        //獲取到根元素
        Element root = document.getRootElement();

        //把新創(chuàng)建的name節(jié)點掛在根節(jié)點下面
        root.add(newElement);

        //創(chuàng)建帶有格式的對象
        OutputFormat outputFormat = OutputFormat.createPrettyPrint();

        //設置編碼,默認的編碼是gb2312播赁,讀寫的編碼不一致颂郎,會導致亂碼的!
        outputFormat.setEncoding("UTF-8");

        //創(chuàng)建XMLWriter對象
        XMLWriter xmlWriter = new XMLWriter(new FileWriter("2.xml"), outputFormat);

        //XMLWriter對象寫入的是document
        xmlWriter.write(document);

        //關閉流
        xmlWriter.close();

    }
  • 效果如下容为,是有格式的乓序!
image

在指定的位置增加節(jié)點!現(xiàn)在我想的就是在age屬性前面添加節(jié)點坎背!


        //創(chuàng)建一個新節(jié)點
        Element element = DocumentHelper.createElement("name");
        element.setText("ouzciheng");

        //獲取得到person下所有的節(jié)點元素替劈!
        List list = document.getRootElement().elements();

        //將節(jié)點添加到指定的位置上
        list.add(1, element);

  • 效果圖:
image


修改

  • XMLWriter和獲取Document對象的代碼我就不貼出來了,反正都是一樣的了得滤!

        //獲取得到age元素
        Element age = document.getRootElement().element("age");
        age.setText("9999");

  • 效果如下:
image

刪除

  • XMLWriter和獲取Document對象的代碼我就不貼出來了陨献,反正都是一樣的了!

        //獲取得到age節(jié)點
        Element age = document.getRootElement().element("age");

        //得到age節(jié)點的父節(jié)點懂更,使用父節(jié)點的remove刪除age節(jié)點眨业!
        age.getParent().remove(age);
  • 效果:
image

XPATH

什么是XPATH

XPath 是一門在 XML 文檔中查找信息的語言。XPath 用于在 XML 文檔中通過元素和屬性進行導航沮协。

為什么我們需要用到XPATH

上面我們使用dom4j的時候龄捡,要獲取某個節(jié)點,都是通過根節(jié)點開始慷暂,一層一層地往下尋找墅茉,這就有些麻煩了

如果我們用到了XPATH這門語言呜呐,要獲取得到XML的節(jié)點就斤,就非常地方便了!


快速入門

使用XPATH需要導入開發(fā)包jaxen-1.1-beta-7蘑辑,我們來看官方的文檔來入門吧洋机。

  • XPATH的文檔非常國際化啊,連中文都有
image
  • XPATH文檔中有非常多的實例洋魂,非常好學灾票,對著來看就知道了收夸!
image
  • 我們來用XPATH技術讀取XML文件的信息吧,XML文檔如下:
image
  • 之前,我們是先獲取根節(jié)點旷余,再獲取guangdong節(jié)點再獲取guangzhou節(jié)點,然后才能讀取tianhe節(jié)點或者luogang節(jié)點的蝴猪,下面我們來看一下使用XPATH可以怎么的便捷投蝉!

        //直接獲取到luogang節(jié)點
        org.dom4j.Node node =  document.selectSingleNode("http://luogang");

        //獲取節(jié)點的內(nèi)容
        String value = node.getText();

        System.out.println(value);
  • 效果:
image

獲取什么類型的節(jié)點,XPATH的字符串應該怎么匹配,查文檔就知道了邦尊,這里就不再贅述了背桐。!


如果文章有錯的地方歡迎指正蝉揍,大家互相交流链峭。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末又沾,一起剝皮案震驚了整個濱河市弊仪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杖刷,老刑警劉巖励饵,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挺勿,居然都是意外死亡曲横,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門不瓶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禾嫉,“玉大人,你說我怎么就攤上這事蚊丐∥醪危” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵麦备,是天一觀的道長孽椰。 經(jīng)常有香客問我,道長凛篙,這世上最難降的妖魔是什么黍匾? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮呛梆,結果婚禮上锐涯,老公的妹妹穿的比我還像新娘。我一直安慰自己填物,他們只是感情好纹腌,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滞磺,像睡著了一般升薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上击困,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天涎劈,我揣著相機與錄音,去河邊找鬼。 笑死责语,一個胖子當著我的面吹牛炮障,可吹牛的內(nèi)容都是我干的目派。 我是一名探鬼主播坤候,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼企蹭!你這毒婦竟也來了白筹?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤谅摄,失蹤者是張志新(化名)和其女友劉穎徒河,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體送漠,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡顽照,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了闽寡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片代兵。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爷狈,靈堂內(nèi)的尸體忽然破棺而出植影,到底是詐尸還是另有隱情,我是刑警寧澤涎永,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布思币,位于F島的核電站,受9級特大地震影響羡微,放射性物質發(fā)生泄漏谷饿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一妈倔、第九天 我趴在偏房一處隱蔽的房頂上張望博投。 院中可真熱鬧,春花似錦启涯、人聲如沸贬堵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黎做。三九已至,卻和暖如春松忍,著一層夾襖步出監(jiān)牢的瞬間蒸殿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宏所,地道東北人酥艳。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像爬骤,于是被迫代替她去往敵國和親充石。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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