XML就是這麼簡單

来源:https://www.cnblogs.com/Java3y/archive/2018/02/10/8439430.html
-Advertisement-
Play Games

什麼是XML? XML:extensiable markup language 被稱作可擴展標記語言 XML簡單的歷史介紹: gml sgml html xml gml(通用標記語言)–在不同的機器進行通信的數據規範 sgml(標準通用標記語言) html(超文本標記語言) 為什麼我們需要使用XML ...


什麼是XML?

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

XML簡單的歷史介紹:

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

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

  • ①我們沒有XML這種語言之前,我們使用的是String作為兩個程式之間的通訊!現在問題就來了,如果我們傳輸的是帶有關係型結構的數據,String怎麼表達呢?String對關係型數據不擅長,要是描述起來也難免會有歧義的時候!關係型數據如圖下所示:

  • HTML語言本身就有缺陷
    • 標記都是固定的,不能自定義。HTML語言中有什麼標記就只能用什麼標記
    • HTML標簽本身就缺少含義(tr標簽裡面什麼內容都能放進去,不規範!!)
    • HTML沒有實現真正的國際化

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


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

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

我們可以發現XML是可以描述很複雜的數據關係的


XML的用途

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

②:程式間數據的傳輸,XML的格式是通用的,能夠減少交換數據時的複雜性!

③:充當小型資料庫,如果我們的數據有時候需要人工配置的,那麼XML充當小型的資料庫是個不錯的選擇,程式直接讀取XML文件顯然要比讀取資料庫要快呢!


XML的技術架構

XML被設計為“什麼都不做”,XML數據或XML文檔只用於組織、存儲數據,除此之外的數據生成、讀取、傳送、存取等等操作都與XML本身無關!

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

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

XML語法:

文檔聲明:

  • XML聲明放在XML的第一行
  • version----版本
  • encoding--編碼
  • standalone--獨立使用--預設是no。standalone表示該xml是不是獨立的,如果是yes,則表示這個XML文檔時獨立的,不能引用外部的DTD規範文件;如果是no,則該XML文檔不是獨立的,表示可以引用外部的DTD規範文檔。

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


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

元素

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

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

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

看起來好像有很多需要值得註意的地方,其實只需要記住:XML的語法是規範的!不要隨意亂寫!


屬性

屬性是作為XML元素中的一部分的,命名規範也是和XML元素一樣的!


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

    </中國>

註釋

註釋和HTML的註釋是一樣的


    <!---->

CDATA

在編寫XML文件時,有些內容可能不想讓解析引擎解析執行,而是當作原始內容處理。遇到此種情況,可以把這些內容放在CDATA區里,對於CDATA區域內的內容,XML解析程式不會處理,而是直接原封不動的輸出

語法:

    
    <![CDATA[
        ...內容
    ]]>

轉義字元

對於一些單個字元,若想顯示其原始樣式,也可以使用轉義的形式予以處理。

處理指令

處理指令,簡稱PI (processing instruction)。處理指令用來指揮解析引擎如何解析XML文檔內容。

例如:

在XML文檔中可以使用xml-stylesheet指令,通知XML解析引擎,應用css文件顯示xml文檔內容。

    <?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;
    }
  • 效果:


JDK中的XML API

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

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

什麼是XML解析

前面XML章節已經說了,XML被設計為“什麼都不做”,XML只用於組織、存儲數據,除此之外的數據生成、讀取、傳送等等的操作都與XML本身無關!

XML解析就是讀取XML的數據!


XML解析方式

XML解析方式分為兩種:

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

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

XML解析操作

從上面的圖很容易發現,應用程式不是直接對XML文檔進行操作的,而是由XML解析器對XML文檔進行分析,然後應用程式通過XML解析器所提供的DOM介面或者SAX介面對分析結果進行操作,從而間接地實現了對XML文檔的訪問!

常用的解析器和解析開發包的關係如下所示


為什麼有3種開發包?

  • jaxp開發包是JDK自帶的,不需要導入開發包。
  • 由於sun公司的jaxp不夠完善,於是就被研發了Jdom。XML解析如果使用Jdom,需要導入開發包
  • dom4j是由於Jdom的開發人員出現了分歧,dom4j由Jdom的一批開發人員所研發。XML解析如果使用Jdom,需要導入開發包【現在用dom4j是最多的!】

jaxp

雖然jaxp解析XML的性能以及開發的簡易度是沒有dom4j好,但是jaxp不管怎麼說都是JDK內置的開發包,我們是需要學習的

DOM解析操作

DOM解析是一個基於對象的API,它把XML的內容載入到記憶體中,生成與XML文檔內容對應的模型!當解析完成,記憶體中會生成與XML文檔的結構與之對應的DOM對象樹,這樣就能夠根據樹的結構,以節點的形式對文檔進行操作!

簡單來說:DOM解析會把XML文檔載入到記憶體中,生成DOM樹的元素都是以對象的形式存在的!我們操作這些對象就能夠操作XML文檔了!

  • 下麵這樣圖就能很好地說明瞭,是怎麼樣生成與XML文檔內容對應的DOM樹!


既然XML文檔的數據是帶有關係型的,那麼生成的DOM樹的節點也是有關係的:

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

在DOM解析中有幾個核心的操作介面:

  • Document【代表整個XML文檔,通過Document節點可以訪問XML文件中所有的元素內容!】
  • Node【Node節點幾乎在XML操作介面中幾乎相當於普通Java類的Object,很多核心介面都實現了它,在下麵的關係圖可以看出!】
  • NodeList【代表著一個節點的集合,通常是一個節點中子節點的集合!】
  • NameNodeMap【表示一組節點和其唯一名稱對應的一一對應關係,主要用於屬性節點的表示(書上說是核心的操作介面,但我好像沒用到!呃呃呃,等我用到了,我再來填坑!)】

節點之間的關係圖:

  • 有人可能會很難理解,為什麼Document介面比Node介面還小,呃呃呃,我是這樣想的:一個Document由無數個Node組成,這樣我也能把Document當成是Node呀!如果實在想不通:人家都這樣設計了,你有種就不用啊!!(開玩笑的.....)

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

  • XML文檔代碼

    <?xml version="1.0" encoding="UTF-8" ?>
    <china>
        <guangzhou >廣州</guangzhou>
        <shenzhen>深圳</shenzhen>
        <beijing>北京</beijing>
        <shanghai>上海</shanghai>
    </china>
  • 根據XML解析的流程圖,我們先要獲取到解析器對象!


    public class DomParse {
    
        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
    
            //API規範:需要用一個工廠來造解析器對象,於是我先造了一個工廠!
            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文檔的內容用來幹嘛?無非就是增刪改查遍歷,只要我們會對XML進行增刪改查,那就說明我們是會使用DOM解析的

遍歷

  • 我們再來看一下XML文檔的內容,如果我們要遍歷該怎麼做?

  • 可能我們會有兩種想法
    • ①:從XML文檔內容的上往下看,看到什麼就輸出什麼!【這正是SAX解析的做法】
    • ②:把XML文檔的內容分成兩部分,一部分是有子節點的,一部分是沒有子節點的(也就是元素節點!)。首先我們判斷是否為元素節點,如果是元素節點就輸出,不是元素節點就獲取到子節點的集合,再判斷子節點集合中的是否是元素節點,如果是元素節點就輸出,如果不是元素節點獲取到該子節點的集合....好的,一不小心就遞歸了...
  • 我們來對XML文檔遍歷一下吧,為了更好地重用,就將它寫成一個方法吧(也是能夠更好地用遞歸實現功能)

    public class DomParse {
    
        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
    
            //API規範:需要用一個工廠來造解析器對象,於是我先造了一個工廠!
            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類型的實例對象吧!多態!!!
        private static void list(Node node) {
    
            //判斷是否是元素節點,如果是元素節點就直接輸出
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                System.out.println(node.getNodeName());
            }
    
            //....如果沒有進入if語句,下麵的肯定就不是元素節點了,所以獲取到子節點集合
            NodeList nodeList = node.getChildNodes();
    
            //遍歷子節點集合
            for (int i = 0; i < nodeList.getLength(); i++) {
    
                //獲取到其中的一個子節點
                Node child = nodeList.item(i);
    
                //...判斷該子節點是否為元素節點,如果是元素節點就輸出,不是元素節點就再獲取到它的子節點集合...遞歸了
    
                list(child);
            }
            
        }
    }
  • 效果:


查詢

現在我要做的就是:讀取guangzhou這個節點的文本內容!



    private static void read(Document document) {

        //獲取到所有名稱為guangzhou節點
        NodeList nodeList = document.getElementsByTagName("guangzhou");
        
        //取出第一個名稱為guangzhou的節點
        Node node = nodeList.item(0);
        
        //獲取到節點的文本內容
        String value = node.getTextContent();

        System.out.println(value);

    }
  • 效果:


增加

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


    private static void add(Document document) {

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

        //向節點添加文本內容
        element.setTextContent("杭州");

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

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

    }
  • 做到這裡,我僅僅在記憶體的Dom樹下添加了一個節點,要想把記憶體中的Dom樹寫到硬碟文件中,需要轉換器

  • 獲取轉換器也十分簡單


        //獲取一個轉換器它需要工廠來造,那麼我就造一個工廠
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        
        //獲取轉換器對象
        Transformer transformer = transformerFactory.newTransformer();
  • 把記憶體中的Dom樹更新到硬碟文件中的transform()方法就稍稍有些複雜了

  • 它需要一個Source實例對象和Result的實例對象,這兩個介面到底是什麼玩意啊?

  • 於是乎,我就去查API,發現DomSource實現了Source介面,我們使用的不正是Dom解析嗎,再看看構造方法,感覺就是它了!

  • 而SteamResult實現了Result介面,有人也會想,DomResult也實現了Result介面啊,為什麼不用DomResult呢?我們現在做的是把記憶體中的Dom樹更新到硬碟文件中呀,當然用的是StreamResult啦!

  • 完整代碼如下:


    private static void add(Document document) throws TransformerException {

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

        //向節點添加文本內容
        element.setTextContent("杭州");

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

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

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

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

        //把記憶體中的Dom樹更新到硬碟中
        transformer.transform(new DOMSource(document),new StreamResult("city.xml"));
    }
  • 效果:


剛剛增加的節點是在china節點的末尾處的,現在我想指定增加節點的在beijing節點之前,是這樣做的:



    private static void add2(Document document) throws TransformerException {

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

        //創建新的節點
        Element element = document.createElement("guangxi");

        //設置節點的文本內容
        element.setTextContent("廣西");

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

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

        //將記憶體中的Dom樹更新到硬碟文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));
        
    }
  • 效果:


刪除

現在我要刪除的是beijing這個節點!



    private static void delete(Document document) throws TransformerException {

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

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

        //把記憶體中的Dom樹更新到硬碟文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));


    }
  • 效果:

修改

將guangzhou節點的文本內容修改成廣州你好


    private static void update(Document document) throws TransformerException {

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

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

        //將記憶體中的Dom樹更新到硬碟文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));


    }
  • 效果:


操作屬性

XML文檔是可能帶有屬性值的,現在我們要guangzhou節點上的屬性

    private static void updateAttribute(Document document) throws TransformerException {

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

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

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

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


        //將記憶體中的Dom樹更新到硬碟文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));


    }
  • 效果:


SAX解析

SAX採用的是一種順序的模式進行訪問,是一種快速讀取XML數據的方式。當時候SAX解析器進行操作時,會觸發一系列事件SAX。採用事件處理的方式解析XML文件,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器

sax是一種推式的機制,你創建一個sax 解析器,解析器在發現xml文檔中的內容時就告訴你(把事件推給你). 如何處理這些內容,由程式員自己決定。

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


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


        //要得到解析器對象就需要造一個工廠,於是我造了一個工廠
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        
        //獲取到解析器對象
        SAXParser saxParse = saxParserFactory.newSAXParser();
  • 調用解析對象的解析方法的時候,需要的不僅僅是XML文檔的路徑!還需要一個事件處理器!

  • 事件處理器都是由我們程式員來編寫的,它一般繼承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("我開始來掃描啦!!!!");
        }
    
        @Override
        public void endDocument() throws SAXException {
    
            System.out.println("我結束了!!!!");
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

            //如果要解析出節點屬性的內容,也非常簡單,只要通過attributes變數就行了!

            //輸出節點的名字!
            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));
        }
    }
  • 我們發現,事件處理器的代碼都非常簡單,然後就如此簡單地就能夠遍歷整個XML文檔了!

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

  • 現在我只想查詢guangzhou節點的內容:


    //定義一個標識量,用於指定查詢某個節點的內容
    boolean flag = false;

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

        //如果節點名稱是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的情況下我才輸出文本的內容
        if (flag == true) {
            System.out.println(new String(ch, start, length));

        }
    }

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

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

        }
    }
  • 效果:


DOM和SAX解析的區別:

DOM解析讀取整個XML文檔,在記憶體中形成DOM樹,很方便地對XML文檔的內容進行增刪改。但如果XML文檔的內容過大,那麼就會導致記憶體溢出!

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

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


dom4j

Dom4j是一個非常優秀的Java XML API,具有性能優異、功能強大和極易使用的特點。

為什麼需要有dom4j

  • dom缺點:比較耗費記憶體

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

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

因為dom4j不是sun公司的產品,所以我們開發dom4j需要導入開發包


獲取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);

③:主動創建document對象.


Document document =DocumentHelper.createDocument();

//創建根節點
Element root = document.addElement("members");

CRUD的重要一句話:

讀取XML文檔的數據,都是通過Document獲取根元素,再通過根元素獲取得到其他節點的,從而進行操作!

如果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);

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

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

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

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

        System.out.println("文本內容是:" + text);
        System.out.println("屬性內容是:" + attribute);
        
    }
  • XML文件如下:

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

  • 多層結構的查詢:

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

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

        String value = element.getText();

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


增加

在DOM4j中要對記憶體中的DOM樹寫到硬碟文件中,也是要有轉換器的支持的!

dom4j提供了XMLWriter供我們對XML文檔進行更新操作,一般地創建XMLWriter的時候我們都會給出兩個參數,一個是Writer,一個是OutputFormat

這個OutputFormat有什麼用的呢?其實就是指定回寫XML的格式和編碼格式。細心的朋友會發現,上面我們在jaxp包下使用dom解析的Transformer類,把記憶體中的DOM樹更新到文件硬碟中,是沒有格式的!不信倒回去看看!這個OutputFormat就可以讓我們更新XML文檔時也能帶有格式


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

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

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

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

        //關閉流
        xmlWriter.close();
  • 下麵我們就為在person節點下新創建一個name節點吧,完整的代碼如下:


    @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);

        //創建出新的節點,為節點設置文本內容
        Element newElement = DocumentHelper.createElement("name");
        newElement.setText("ouzicheng");

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

        //把新創建的name節點掛在根節點下麵
        root.add(newElement);

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

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

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

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

        //關閉流
        xmlWriter.close();


    }
  • 效果如下,是有格式的


在指定的位置增加節點!現在我想的就是在age屬性前面添加節點!


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

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

        //將節點添加到指定的位置上
        list.add(1, element);
  • 效果圖:



修改

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

        //獲取得到age元素
        Element age = document.getRootElement().element("age");
        age.setText("9999");
  • 效果如下:


刪除

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

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

        //得到age節點的父節點,使用父節點的remove刪除age節點!
        age.getParent().remove(age);
  • 效果:


XPATH

什麼是XPATH

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

為什麼我們需要用到XPATH

上面我們使用dom4j的時候,要獲取某個節點,都是通過根節點開始,一層一層地往下尋找,這就有些麻煩了

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


快速入門

使用XPATH需要導入開發包jaxen-1.1-beta-7,我們來看官方的文檔來入門吧。

  • XPATH的文檔非常國際化啊,連中文都有

  • XPATH文檔中有非常多的實例,非常好學,對著來看就知道了!

  • 我們來用XPATH技術讀取XML文件的信息吧,XML文檔如下:

  • 之前,我們是先獲取根節點,再獲取guangdong節點再獲取guangzhou節點,然後才能讀取tianhe節點或者luogang節點的,下麵我們來看一下使用XPATH可以怎麼的便捷

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

        //獲取節點的內容
        String value = node.getText();

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

獲取什麼類型的節點,XPATH的字元串應該怎麼匹配,查文檔就知道了,這裡就不再贅述了。!


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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java中成員訪問許可權 Java中的訪問許可權控制符有四個:作用域lxx__當前類____同一package___子孫類____其他package publiclxx___√lxx__lxx___√lxx__lxx__√lxx__lxx___√ protected___√lxx__lxx___√___ ...
  • ssh服務端 ssh客戶端 socket文件傳輸並校驗 服務端 socket文件傳輸並校驗 客戶端 ...
  • IOCP全稱IOCP全稱I/O Completion Port,中文譯為I/O完成埠。IOCP是一個非同步I/O的Windows I/O模型,它可以自動處理I/O操作,併在I/O操作完成後將完成通知發送給用戶。 ...
  • 資料庫準備—創建db_login資料庫 t_user表 1、創建web工程 2、創建用戶model user.java 3、創建util包 Dbutil.java 4、創建dao包 UserDao.java 5、創建servlet包 loginServlet 6、用戶登錄界面 login.jsp 跳 ...
  • 題目描述 佳媛姐姐過生日的時候,她的小伙伴從某寶上買了一個有趣的玩具送給他。玩具上有一個數列,數列中某些項的值可能會變化,但同一個時刻最多只有一個值發生變化。現在佳媛姐姐已經研究出了所有變化的可能性,她想請教你,能否選出一個子序列,使得在任意一種變化中,這個子序列都是不降的?請你告訴她這個子序列的最 ...
  • Python介紹 python的創始人為吉多·範羅蘇姆(Guido van Rossum)。1989年的聖誕節期間,吉多·範羅蘇姆為了在阿姆斯特丹打發時間,決心開發一個新的腳本解釋程式,作為ABC語言的一種繼承。 Python可以應用於眾多領域,如:數據分析、組件集成、網路服務、圖像處理、數值計算和 ...
  • 1. 打開文件 函數定義:FILE *fopen(char *pname, char *mode) 函數說明:pname 是文件名,mode 是打開文件的方式( r:只讀,w:只寫) 返回值:若返回 NULL,則打開失敗。 2. 關閉文件 函數定義:int fclose(FILE *stream) ...
  • 題目描述 給定一棵N個節點的樹,每個點有一個權值,對於M個詢問(u,v,k),你需要回答u xor lastans和v這兩個節點間第K小的點權。其中lastans是上一個詢問的答案,初始為0,即第一個詢問的u是明文。 輸入輸出格式 輸入格式: 第一行兩個整數N,M。 第二行有N個整數,其中第i個整數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...