xml解析詳解

1. XML總結(jié)

1.1. XML簡介

XML : 可擴展的標記語言。(和HTML非常類似的)

  • 可擴展的。
  • 自定義的標簽。

與HTML區(qū)別: XML傳輸數(shù)據(jù),HTML是顯示數(shù)據(jù)。

XML的版本: XML1.0(幾乎都使用該版本) XML1.1(不向下兼容)

做什么用?=>描述有關系的數(shù)據(jù)

應用

  1. 作為配置文件。
  2. 可以在系統(tǒng)與系統(tǒng)之間進行數(shù)據(jù)的傳輸。
    • webserivice soap XML封裝數(shù)據(jù)
    • json 和XML概念相似

1.2. XML基本語法

  • 文檔聲明(*****)
    • 寫法: <?xml version="1.0" ?> 注意:<? 和xml之間不能有空格. xml和version、encoding、standalone 必須全部小寫

    • 文檔聲明必須出現(xiàn)在xml文件的|--第一行和第一列--|(即該語句前面不能有空格,空行)的位置。

    • 屬性:

      • version="1.0" XML的版本 (必須寫)
      • encoding="UTF-8" 編碼集 (可選的)決定解碼的方式
      • standalone="yes或者no" 代表xml的文件是否是獨立的。(如果是no,不獨立,可以引入外部的文件)(可選的)
        • 該屬性沒什么卵用,因為不寫該屬性,也可以引入外部的文件,即該值默認為 no 。
    • 亂碼會伴隨你們一生?

      • 產(chǎn)生的原因:保存文件時和打開文件時采用的編碼不一致。
      • 解決辦法:保存文件和打開文件采用的編碼一致就ok。(MyEclipse不會產(chǎn)生亂碼問題,自動轉(zhuǎn)換好了)
  • 元素(***)
    • 開始標簽和結(jié)束標簽。

      • 包含標簽主體: <abc>文本</abc>
      • 不包含標簽主體: <abc/>
    • 不能交叉嵌套

    • 只能有一個根元素(必須有,并且只能有一個)

    • 命名規(guī)范:

      • 區(qū)分大小寫 錯誤的:<a></A> 代表兩個標簽
      • 不能以數(shù)字和-中劃線開頭 錯誤的:<1a> <-a>
      • 不能以XML(Xml XML xml)開頭 錯誤的:<xmlaa>
      • 不能包含空格和冒號。
  • 屬性(***)
    • 自定義:命名規(guī)范同上。
    • 在同一個元素上,不能有相同的屬性。(*****)
    • 屬性值可以使用雙引號或者單引號。
  • 注釋(*)

    • 和HTML的注釋相同

    • 注釋不能嵌套。

  • 特殊字符
    轉(zhuǎn)義字符

    •   <               <
      
    •   >               >
      
    •   &               &
      
    •   單個雙引號           "
      
    •   單個單引號           '
      

其中 < 和 & 符號必須轉(zhuǎn)義,其他三個可以不用轉(zhuǎn)義

如何在網(wǎng)頁上顯示特定的字符串?(包含一堆特殊字符,全部轉(zhuǎn)義特別麻煩)

  • CDATA區(qū)

    • 把標簽中的內(nèi)容作為字符串。
    • 語法:
        <![CDATA[
            內(nèi)容:當成字符串直接顯示
        ]]>
    

    例:

        <![CDATA[
            if(2<3&&32>8){
                "das"&&'ds';
            }
        ]]>
    
  • PI(處理指令)(沒用)

    • 替換HTML

1.3. XML約束

約束的分類

  • DTD
  • schema

<myspring>
<bean>hello.java</bean>
<貓/>
</myspring>

  • 格式良好的XML:遵循語法規(guī)范。
  • 有效的XML:有約束。

1.3.1. XML約束--DTD 約束

  • DTD的約束
    • 快速入門

    • 快速入門的步驟:

      • 需要出現(xiàn)哪些標簽?

      • 在DTD的文件中編寫元素
        <!ELEMENT 元素名稱 (元素類型)>

      • 判斷元素是否是復雜還是簡單元素?

        • 如果是簡單元素:(#PCDATA) 代表是字符串,必須有()
        • 如果是復雜元素:(子節(jié)點1,子節(jié)點2 ...) 內(nèi)部是子結(jié)點的元素名稱
      • 需要在book.xml引入外部DTD的文件

        • <!DOCTYPE 根節(jié)點元素名 SYSTEM "DTD文件的地址">
    • DTD與XML文檔的關聯(lián)方式

      • 可以在XML的文件中直接書寫DTD的代碼。(經(jīng)常使用)
        <!DOCTYPE 根節(jié)點 [
        DTD的代碼
        ]>

      • 引入本地的DTD文件(經(jīng)常使用)
        <!DOCTYPE 根節(jié)點 SYSTEM "DTD文件的地址">

      • 引入網(wǎng)絡上的DTD文件(偶爾使用)
        <!DOCTYPE 根節(jié)點 PUBLIC "DTD文件名稱" "DTD文件的地址">

* 元素定義
    * 語法:<!ELEMENT 元素名稱 元素類型>

        * (#PCDATA)     字符串,中間可以為空
        * EMPTY         空,標簽內(nèi)部不能包含數(shù)據(jù),不能包括空格、換行
        * ANY           任意的,中間可以為空,可以為字符串,可以為(子元素)
        * (子元素)

        * 子元素:
            * 子元素之間的關系
                    ,       子元素出現(xiàn)是有順序的
                    |       子元素只能出現(xiàn)一個

            * 子元素出現(xiàn)的次數(shù)
                    +       子元素出現(xiàn)1次或多次
                    *       子元素出現(xiàn)0次或多次
                    ?       子元素出現(xiàn)0次或1次

            在子元素名后,加上數(shù)量的限定

            ```xml
            <!ELEMENT MYFILE ((TITLE*, AUTHOR?, EMAIL)* | COMMENT)>

                <MYFILE>
                    <TITLE></TITLE>
                    <AUTHOR></AUTHOR>
                    <EMAIL></EMAIL>
                    <TITLE></TITLE>
                    <AUTHOR></AUTHOR>
                    <EMAIL></EMAIL>
                    <TITLE></TITLE>
                    <AUTHOR></AUTHOR>
                    <EMAIL></EMAIL>
                </MYFILE>
            ```
* 屬性定義(AttributeList)

    * 寫法:   
        ```xml
            <!ATTLIST 元素名稱
                屬性名稱 屬性類型 屬性約束      和數(shù)據(jù)庫中表中數(shù)據(jù)的定義 相似
                屬性名稱 屬性類型 屬性約束
            >
        ```
    * 屬性類型
        *  CDATA        字符串
        *  枚舉(沒有提供關鍵字)  (男人|女人)-->枚舉可能結(jié)果視為枚舉類型聲明
        *  ID   代表唯一的值,不能以數(shù)字開頭

    * 屬性的約束

        *  #IMPLIED     可選的
        *  #REQUIRED        必須出現(xiàn)的

        以下兩個屬性,在前面定義了,即使相應的元素中沒有寫,也存在,相應屬性是固定值/默認值
        *  #FIXED       固定值 ,格式為: #FIXED "值"
        *  默認值     直接在屬性類型后加上該默認值 (很少用)

        注意:一個元素內(nèi)只能有一個 ID 屬性,且 ID 屬性后面的約束只能是
                #REQUIRED 或者 #IMPLIED,不能是 #FIXED 或者 默認值

        例:
        ```xml
         <!ARRLIST 書
            頁數(shù)  CDATA  #REQUIRED           必須出現(xiàn)的
            贈送  (贈送|非贈送) #IMPLIED     可選
            編號  ID #REQUIRED               ID類型
            二維碼  CDATA #FIXED "哈哈哈"    固定值
            出版國家  CDATA "中國"           默認值
        >
        ```
    * 實體定義(用的不多)
            *  <!ENTITY 別名 "值" >
                在元素中引用 &別名;   最終在瀏覽器中顯示的是 "值"

            *  需要在xml中引入別名,瀏覽器打開文件后,在引入的位置上顯示值的。

        例:
            dtd 內(nèi)容:
            <!DOCTYPE 書架 [
                <!ELEMENT  ...>

                <!ATTLIST ...>

                <!ENTITY haha "臥槽">
            ]>

            xml 內(nèi)容:
                <書>&haha;</書>
                 這里的&haha; 在瀏覽器中顯示為 "臥槽"

示例: student.dtd


    <?xml version="1.0" encoding="UTF-8"?>
    <!ELEMENT  班級 (學生+)>
    <!ELEMENT  學生 (姓名,年齡,身高,體重)>
    <!ELEMENT  姓名 (#PCDATA)>
    <!ELEMENT  年齡 (#PCDATA)>
    <!ELEMENT  身高 (#PCDATA)>
    <!ELEMENT  體重 (#PCDATA)>


    <!ATTLIST  班級
        編號  ID #REQUIRED
        年級  CDATA "三年級"
        學校名 CDATA  #FIXED "西北工業(yè)大學"

    >

    <!ATTLIST  學生
            編號  ID #REQUIRED
            性別  (男|女)  #REQUIRED
            癖好 CDATA  #IMPLIED
    >

對應的 xml文件:

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

<班級 編號="des3" 年級="大一" 學校='西工大' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                         xmlns="http://www.example.org/student" 
                         xsi:schemaLocation=" http://www.example.org/student  student.xsd">
    <學生 編號="d3" 性別="女" >
        <姓名>阿嬌</姓名>
        <年齡>50</年齡>
        <身高>150</身高>
        <體重>40</體重>
        
    </學生>
    
    <學生 編號="df3" 性別="男">
        <姓名>薛鵬</姓名>
        <年齡>55</年齡>
        <身高>175</身高>
        <體重>70.32</體重>
    </學生>

</班級>

1.3.2. XML約束--schema 約束

  • schema約束:
    • schema和DTD的對比(面試題):
      • schema符合XML的語法結(jié)構(gòu)。
      • DOM、SAX 可以很好地解析schema文檔。
      • schema對名稱空間支持的好。
      • schem支持更多的數(shù)據(jù)類型,自定義的數(shù)據(jù)類型。
* 預先定義元素和屬性
* schema的后綴名是.xsd
* 必須只能有一個根節(jié)點,名稱是schema。
  • 開發(fā)步驟
    • 開發(fā)schema的約束文檔

    • schema文檔聲明

      <schema xmlns ="http://www.w3.org/2001/XMLSchema"
              targetNamespace="http://www.itcast.cn/1110"
              elementFormDefault="qualified"
      >
      
    • 引入W3C的名稱

    • 起名:targetNamespace 目標名稱空間(起名)

      • elementFormDefault
        qualified(使用):質(zhì)量好的
        unqualified :質(zhì)量不好的↑
    • 在XML文檔中的--“根標簽”---引入自己編寫的schema文檔

        <根標簽 屬性1="值1" 屬性2="值2"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns="http://www.itcast.cn/1110"
                xsi:schemaLocation="http://www.itcast.cn/1110 student.xsd"
        >
    
  1. 引入W3C名稱空間,我是實例文檔。注意多了個 -instance

  2. 引入自己編寫的schema的文檔

    • xmlns="http://www.itcast.cn/1110" xsd文檔中的 targetNamespace 內(nèi)容

    • 問題:元素上不能有相同的屬性名稱

      • 解決:起別名 :aa
      • 技巧:在下面出現(xiàn)標簽的概率小起別名
  3. 引入自己編寫的schema文檔的地址

    • schemaLocation屬性是W3C提供的,如果W3C名稱空間要是有別名的話,先把別名寫上。
      xsi:schemaLocation="名稱空間 schema文件的地址"

      • 名稱空間的概念

        • 編寫完schema文檔,起唯一的名稱空間。
        • 在編寫XML文檔,通過xmlns來引入名稱空間。
      • 當標簽為簡單標簽,沒有屬性時,簡單文本的類型可直接在 主標簽中定義
        <element name="name" type="double"></element>
        表示標簽名為 name,標簽內(nèi)內(nèi)容為double 類型

      • 當標簽有屬性,不包含子標簽時,也必須設置成complexType,
        且attribute屬性必須在文本類型標簽extension內(nèi),

        <element name="name">
            <complexType>
                <simpleContent>
                    <extension base="string">
                        <attribute name="salary" type="double" use="required"></attribute>  <!-- attribute位置-->
                    </extension>
                </simpleContent>
            </complexType>
        </element>
        
      • 當標簽有屬性,且包含子標簽時, 需要Sequence/choice/all標簽,attribute標簽必須在sequence結(jié)束標簽和complexType標簽之間,

          <element name="父標簽">
              <complexType>
              <sequence>
                  <element name="子標簽1"></element>
                  <element name="子標簽2"></element>
                  <element name="子標簽3"></element>
              </sequence>
             <attribute name="身高" ></attribute>     <!--  attribute位置-->
              </complexType>
      
          </element>
      
student.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/student"
    xmlns:tns="http://www.example.org/student" elementFormDefault="qualified">

    <element name="班級" >
        <complexType>
            <sequence>
                <element name="學生" maxOccurs="2">
                    <complexType>
                        <sequence>
                            <element name="姓名" type="string"></element>
                            <element name="年齡" type="int"></element>
                            <element name="身高" type="double"> </element>
                            <element name="體重" type="double"></element>
                        </sequence>
                        <attribute name="編號" type="string" use="required"> </attribute>
                        <attribute name="性別" type="string" use="required"> </attribute>
                    </complexType>
                </element>
            </sequence>
            <attribute name="編號" type="string" use="required"></attribute>
            <attribute name="年級" type="string" use= "required"></attribute>
            <attribute name="學校" type="string" ></attribute>

        </complexType>
    </element>
</schema>

student.xml

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

<班級 編號="des3" 年級="大一" 學校='西工大'
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xmlns="http://www.example.org/student"
                         xsi:schemaLocation=" http://www.example.org/student  student.xsd"
>
    <學生 編號="d3" 性別="女" >
        <姓名>阿嬌</姓名>
        <年齡>50</年齡>
        <身高>150</身高>
        <體重>40</體重>

    </學生>

    <學生 編號="df3" 性別="男">
        <姓名>薛鵬</姓名>
        <年齡>55</年齡>
        <身高>175</身高>
        <體重>70.32</體重>
    </學生>

</班級>

1.4. XML 解析(重點)

1.4.1. 解析方式簡介

  • 常用的有兩種?DOM和SAX
  • 區(qū)別:
    • DOM解析XML

      • 在內(nèi)存中形成樹狀結(jié)構(gòu)
      • 缺點:如果文檔過大,容易產(chǎn)生內(nèi)存溢出的問題。
      • 優(yōu)點:方便做增刪改的操作
    • SAX解析

      • 基于事件驅(qū)動,邊讀邊解析
      • 優(yōu)點:不會產(chǎn)生內(nèi)存溢出問題。
      • 缺點:不能做增刪改操作。

(DOM4J在內(nèi)存生成樹狀結(jié)構(gòu),但是很少出現(xiàn)內(nèi)存溢出,故一般使用 DOM4J 解析 xml)

1.4.1.1. DOM(JAXP Crimson解析器)

DOM是用與平臺和語言無關的方式表示XML文檔的官方W3C標準。DOM是以層次結(jié)構(gòu)組織的節(jié)點或信息片斷的集合。這個層次結(jié)構(gòu)允許開發(fā)人員在樹中尋找特定信息。分析該結(jié)構(gòu)通常需要加載整個文檔和構(gòu)造層次結(jié)構(gòu),然后才能做任何工作。由于它是基于信息層次的,因而DOM被認為是基于樹或基于對象的。DOM以及廣義的基于樹的處理具有幾個優(yōu)點。首先,由于樹在內(nèi)存中是持久的,因此可以修改它以便應用程序能對數(shù)據(jù)和結(jié)構(gòu)作出更改。它還可以在任何時候在樹中上下導航,而不是像SAX那樣是一次性的處理。DOM使用起來也要簡單得多。

1.4.1.2. SAX

SAX處理的優(yōu)點非常類似于流媒體的優(yōu)點。分析能夠立即開始,而不是等待所有的數(shù)據(jù)被處理。而且,由于應用程序只是在讀取數(shù)據(jù)時檢查數(shù)據(jù),因此不需要將數(shù)據(jù)存儲在內(nèi)存中。這對于大型文檔來說是個巨大的優(yōu)點。事實上,應用程序甚至不必解析整個文檔;它可以在某個條件得到滿足時停止解析。一般來說,SAX還比它的替代者DOM快許多。

選擇DOM還是選擇SAX? 對于需要自己編寫代碼來處理XML文檔的開發(fā)人員來說, 選擇DOM還是SAX解析模型是一個非常重要的設計決策。 DOM采用建立樹形結(jié)構(gòu)的方式訪問XML文檔,而SAX采用的事件模型。

各自的優(yōu)點/缺點:

  • DOM解析器把XML文檔轉(zhuǎn)化為一個包含其內(nèi)容的樹,并可以對樹進行遍歷。用DOM解析模型的優(yōu)點是編程容易,開發(fā)人員只需要調(diào)用建樹的指令,然后利用navigation APIs訪問所需的樹節(jié)點來完成任務??梢院苋菀椎奶砑雍托薷臉渲械脑?。然而由于使用DOM解析器的時候需要處理整個XML文檔,所以對性能和內(nèi)存的要求比較高,尤其是遇到很大的XML文件的時候。由于它的遍歷能力,DOM解析器常用于XML文檔需要頻繁的改變的服務中。

  • SAX解析器采用了基于事件的模型,它在解析XML文檔的時候可以觸發(fā)一系列的事件,當發(fā)現(xiàn)給定的tag的時候,它可以激活一個回調(diào)方法,告訴該方法制定的標簽已經(jīng)找到。SAX對內(nèi)存的要求通常會比較低,因為它讓開發(fā)人員自己來決定所要處理的tag。特別是當開發(fā)人員只需要處理文檔中所包含的部分數(shù)據(jù)時,SAX這種擴展能力得到了更好的體現(xiàn)。但用SAX解析器的時候編碼工作會比較困難,而且很難同時訪問同一個文檔中的多處不同數(shù)據(jù)。

1.4.1.3. JDOM

JDOM的目的是成為Java特定文檔模型,它簡化與XML的交互并且比使用DOM實現(xiàn)更快。由于是第一個Java特定模型,JDOM一直得到大力推廣和促進。正在考慮通過“Java規(guī)范請求JSR-102”將它最終用作“Java標準擴展”。從2000年初就已經(jīng)開始了JDOM開發(fā)。

JDOM與DOM主要有兩方面不同。首先,JDOM僅使用具體類而不使用接口。這在某些方面簡化了API,但是也限制了靈活性。第二,API大量使用了Collections類,簡化了那些已經(jīng)熟悉這些類的Java開發(fā)者的使用。

JDOM文檔聲明其目的是“使用20%(或更少)的精力解決80%(或更多)Java/XML問題”(根據(jù)學習曲線假定為20%)。JDOM對于大多數(shù)Java/XML應用程序來說當然是有用的,并且大多數(shù)開發(fā)者發(fā)現(xiàn)API比DOM容易理解得多。JDOM還包括對程序行為的相當廣泛檢查以防止用戶做任何在XML中無意義的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情況下的錯誤)。這也許是比學習DOM或JDOM接口都更有意義的工作。

JDOM自身不包含解析器。它通常使用SAX2解析器來解析和驗證輸入XML文檔(盡管它還可以將以前構(gòu)造的DOM表示作為輸入)。它包含一些轉(zhuǎn)換器以將JDOM表示輸出成SAX2事件流、DOM模型或XML文本文檔。JDOM是在Apache許可證變體下發(fā)布的開放源碼。

1.4.1.4. DOM4J(重點)

雖然DOM4J代表了完全獨立的開發(fā)結(jié)果,但最初,它是JDOM的一種智能分支。它合并了許多超出基本XML文檔表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文檔或流化文檔的基于事件的處理。它還提供了構(gòu)建文檔表示的選項,它通過DOM4J API和標準DOM接口具有并行訪問功能。從2000下半年開始,它就一直處于開發(fā)之中。

為支持所有這些功能,DOM4J使用接口和抽象基本類方法。DOM4J大量使用了API中的Collections類,但是在許多情況下,它還提供一些替代方法以允許更好的性能或更直接的編碼方法。直接好處是,雖然DOM4J付出了更復雜的API的代價,但是它提供了比JDOM大得多的靈活性。

在添加靈活性、XPath集成和對大文檔處理的目標時,DOM4J的目標與JDOM是一樣的:針對Java開發(fā)者的易用性和直觀操作。它還致力于成為比JDOM更完整的解決方案,實現(xiàn)在本質(zhì)上處理所有Java/XML問題的目標。在完成該目標時,它比JDOM更少強調(diào)防止不正確的應用程序行為。

DOM4J是一個非常非常優(yōu)秀的Java XML API,具有性能優(yōu)異、功能強大和極端易用使用的特點,同時它也是一個開放源代碼的軟件。如今你可以看到越來越多的Java軟件都在使用DOM4J來讀寫XML,特別值得一提的是連Sun的JAXM也在用DOM4J。

1.4.2. 解析

1.4.2.1. JAXP 下的 DOM 解析

解析

  1. 獲取 DocumentBuilderFactory(解析器工廠類)對象
    public static DocumentBuilderFactory newInstance();     獲取 DocumentBuilderFactory 的新實例。
  1. 獲取 DocumentBuilder(解析器)對象
    public abstract DocumentBuilder newDocumentBuilder() (雖然是抽象方法,但多態(tài)調(diào)用子類的實現(xiàn)方法)使用當前配置的參數(shù)創(chuàng)建一個新的 DocumentBuilder 實例。
  1. 解析XML文檔
    public Document parse(String path)      解析XML文檔,返回Document對象
  1. 根據(jù)需求獲得NodeList 集合
    NodeList nodeList = document.getElementsByTagName("作者");
  1. 遍歷NodeList集合 獲取每個Node內(nèi)的內(nèi)容
    NodeList:
        Node item(int index);  根據(jù)下標獲取NodeList集合中的Node對象
    Node:
        String getTextContent();  獲取Node對象(標簽)中的文本內(nèi)容

    for(int i=0;i<nodeList.getLength();i++){
        Node node = nodeList.item(i);
        System.out.println(node.getTextContent());
    }

示例:

    解析XML(Document parse(String uri) )

    // 獲取解析器工廠類
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    // 獲取解析器對象
    DocumentBuilder builder = factory.newDocumentBuilder();

    // 解析XML的文檔,返回document對象
    Document document = builder.parse("src/book2.xml");

    // 獲取作者元素對象的集合,返回NodeList,注意返回的是NodeList(ELement的父類)而不是Element

    NodeList nodeList = document.getElementsByTagName("作者");

    // 循環(huán)遍歷,拿到每一個作者,打印文本的內(nèi)容,getTextContent()
    for(int i=0;i<nodeList.getLength();i++){
        Node node = nodeList.item(i);
        System.out.println(node.getTextContent());
    }
    //獲取標簽的屬性值
    Document doc = JaxpDomutil.getDocument("src/book1.xml");
    Node node = doc.getElementsByTagName("書").item(0);

    NamedNodeMap attMap = node.getAttributes();
            注意這里返回的是NamedNodeMap子類對象(實際上是屬性集合)
    System.out.println("--------------------");
    System.out.println(attMap.getLength());
    for(int i=0;i<attMap.getLength();i++){
        Node att=attMap.item(i);                  node ---content
        System.out.println(att.getTextContent());
    }


    //遞歸得到 Node 中的所有節(jié)點名稱(前序遍歷)
    public static void getNodeName(Node node){

        if(node.getNodeType() == Node.ELEMENT_NODE){
            System.out.println(node.getNodeName());
        }

        NodeList nodeList = node.getChildNodes();

        for(int i=0;i<nodeList.getLength();i++){
            Node child = nodeList.item(i);
            getNodeName(child);
        }
    }

回寫

  • 獲取回寫的工廠類
  • 獲取回寫對象
  • 調(diào)用回寫的方法進行回寫。

修改 document對象中的內(nèi)容之后進行回寫

    // 創(chuàng)建回寫類的工廠
    TransformerFactory transformerFactory =  TransformerFactory.newInstance();

    // 獲取回寫類
    Transformer transformer = transformerFactory.newTransformer();

    // 調(diào)用回寫的方法
    transformer.transform(new DOMSource(document), new StreamResult("src/book2.xml"));
//將獲取Document對象和 回寫封裝到方法中
public class JaxpDomutil {

    public static Document getDocument(String path) throws Exception{
    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = builderFactory.newDocumentBuilder();
    return builder.parse(path);
    }

    public static void writeXML(Document doc,String path) throws Exception{

    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    transformer.transform(new DOMSource(doc), new StreamResult(path));

    }
    //  在指定的節(jié)點之前添加子節(jié)點
    public static void InsertBefore(Node newNode,Node refNode){
        Node parentNode = refNode.getParentNode();
        parentNode.insertBefore(newNode, refNode);
    }

}

1.4.2.2. JAXP 下的 SAX 解析

SAX的解析原理:

解析器采用SAX方式在解析某個XML文檔時,它只要解析到XML文檔的一個組成部分(邊讀邊解析)
都會去調(diào)用事件處理器的一個方法,解析器在調(diào)用事件處理器的方法時,
會把當前解析到的xml文件內(nèi)容作為方法的參數(shù)傳遞給事件處理器。

事件處理器由程序員編寫,程序員通過事件處理器中方法的參數(shù),
就可以很輕松地得到sax解析器解析到的數(shù)據(jù),從而可以決定如何對數(shù)據(jù)進行處理。

解析過程

  1. 獲取解析器的工廠
    SAXParserFactory saxParseFactory = SAXParserFactory.newInstance();
  1. 獲取解析器對象
    SAXParser saxParser = saxParseFactory.newSAXParser();
  1. 解析XML(XML的文件的地址,事件處理器)
    saxParser.parse(path,new MyHandler());   // 其中MyHandler() 繼承自DefaultHandler()
  • 事件處理器(DefaultHandler)?
    自己編寫的類MyHandler(),需要繼承DefalutHandler類,重寫三個方法。

    • startElement(qName) 開始標簽名
    • characters(char[] ch,int start,int length) 標簽內(nèi)部文本內(nèi)容
    • endElement(qName) 結(jié)束標簽名

    通過傳進來的參數(shù)qName、char[] 、qName 獲取開始標簽內(nèi)容、文本內(nèi)容、結(jié)束標簽內(nèi)容

1.4.3. DOM4j

  • DOM4J的解析(必須會,企業(yè)中用的多)
    • 先下載DOM4J相應的jar包。導入工程中,才能使用。
    • 把dom4j-1.6.1.jar導入到工程中。
    • WEB項目:復制dom4j-1.6.1.jar到 WebRoot -- WEB-INF -- lib里面。就ok。
    • Java項目:右鍵工程項目,選擇Build Path

解析過程

  1. 獲取解析器對象
    SAXReader saxReader=new SAXReader();
  1. 解析獲取Document對象
    Document doc = saxReader.read("src/book1.xml");
  1. 獲取文檔根結(jié)點
    Element root = doc.getRootElement();

DOM4j中,獲得Document對象的方式有三種:

  1. 讀取XML文件,獲得document對象
    SAXReader reader = new SAXReader();
    Document  document = reader.read(new File("input.xml"));
  1. 解析XML形式的文本,得到document對象.
    String text = "<members></members>";
    Document document = DocumentHelper.parseText(text);
  1. 主動創(chuàng)建document對象.
    Document document = DocumentHelper.createDocument();  //創(chuàng)建根節(jié)點
    Element root = document.addElement("members");

節(jié)點方法

  1. 獲取文檔的根節(jié)點.
    Element root = document.getRootElement();
  1. 取得某個節(jié)點的子節(jié)點.
    Element element=root.element("書名");
  1. 取得節(jié)點的文本內(nèi)容
    String text=node.getText();
  1. 取得某節(jié)點下所有名為“member”的子節(jié)點,并進行遍歷.
    List nodes = rootElm.elements("member");         -->獲取所有子元素結(jié)點

    for (Iterator it = nodes.iterator(); it.hasNext();) {
                Element elm = (Element) it.next();
            // do something
    }
  1. 對某節(jié)點下的所有子節(jié)點進行遍歷.
    for(Iterator it=root.elementIterator();it.hasNext();){
            Element element = (Element) it.next();
        // do something??
    }
  1. 在某節(jié)點下添加子節(jié)點.
    Element ageElm = newMemberElm.addElement("age");
    (可通過父結(jié)點root添加
        List<Element> eles = root.elements();
        Element newEle=DocumentHelper.createElement("age");
        newEle.setText("16");
        eles.add(1,newEle);         -->插入子節(jié)點集合中的1位置
        )
  1. 設置節(jié)點文字.
    element.setText("29");
  1. 刪除某節(jié)點.
    parentElm.remove(childElm);     //childElm是待刪除的節(jié)點,parentElm是其父節(jié)點
  1. 添加一個CDATA節(jié)點.
    Element contentElm = infoElm.addElement("content");
    contentElm.addCDATA(diary.getContent());

屬性方法:

        attribute.getQualifiedName()       getValue()/getText()
                            屬性名                   屬性值
  1. 取得某節(jié)點下的某屬性
    Element root=document.getRootElement();     //屬性名name
    Attribute attribute=root.attribute("size");
  1. 取得屬性的文字
    String text=attribute.getText();
  1. 刪除某屬性
    Attribute attribute=root.attribute("size");
    root.remove(attribute);
  1. 遍歷某節(jié)點的所有屬性
    Element root=document.getRootElement();
    方法一:推薦
    for(int i=0;i<root.attributeCount();i++){
        Attribute attribute = root.attribute(i);
        System.out.println(attribute.getQualifiedName()+"\""+attribute.getValue+"\"");
    }

    方法二:
    for(Iterator it=root.attributeIterator();it.hasNext();){
        Attribute attribute = (Attribute) it.next();
        String text=attribute.getText();
        System.out.println(text);
    }
  1. 設置某節(jié)點的屬性和文字.
    newMemberElm.addAttribute("name", "sitinspring");
  1. 設置屬性的文字
    Attribute attribute=root.attribute("name");    ---> 獲取屬性名為 name 的屬性對象
    attribute.setText("sitinspring");              ---> 設置屬性值

將文檔寫入XML

  1. 文檔中全為英文,不設置編碼,直接寫入的形式.
XMLWriter writer = new XMLWriter(new  FileWriter("output.xml"));
writer.write(document);
writer.close();
  1. 文檔中含有中文,設置編碼格式寫入的形式.
    OutputFormat format = OutputFormat.createPrettyPrint();       // 指定XML編碼
    format.setEncoding("GBK");
    XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);
    writer.write(document);
    writer.close();

字符串與XML的轉(zhuǎn)換

  1. 將字符串轉(zhuǎn)化為XML
    String text = "<members> <member>sitinspring</member></members>";
    Document document = DocumentHelper.parseText(text);

2.將文檔或節(jié)點的XML轉(zhuǎn)化為字符串.

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

    String docXmlText=document.asXML();      --> 將XML文檔轉(zhuǎn)換成XML格式的字符串

    Element root=document.getRootElement();
    String rootXmlText=root.asXML();          --> 將根結(jié)點轉(zhuǎn)換成XML格式的字符串
    Element memberElm=root.element("member");
    String memberXmlText=memberElm.asXML();  --> 將元素結(jié)點轉(zhuǎn)換成XML格式的字符串
  • DOM4J對XPATH的支持
    • 導入包。jaxen-1.1-beta-6.jar。

    • 怎么使用?
      selectNodes("/AAA") 返回集合
      selectSingleNode() 一個Node對象

    • 參數(shù)就是xpath的語法
      / 考慮層級關系 / / 不考慮層級關系

        /AAA/BBB            獲取BBB的節(jié)點
        //AAA/BBB         A不考慮層級,B考慮層級
        / /BBB              無論層級關系,找到BBB的節(jié)點
        *                   代表是所有
      
        /AAA/BBB[1]     找到BBB的第一個    /AAA/BBB[last()]   最后一個
        上面這種只能用單斜線 不能用雙斜線
      
        @                   屬性
        /*/*/*/BBB  代表三層標簽下的 BBB標簽
      

將文檔寫入XML
1.文檔中全為英文,不設置編碼,直接寫入的形式.

    XMLWriter writer = new XMLWriter(new  FileWriter("output.xml"));
    writer.write(document);
    writer.close();

2.文檔中含有中文,設置編碼格式寫入的形式.

    OutputFormat format = OutputFormat.createPrettyPrint();?// 指定XML編碼????? ???????????? 
    format.setEncoding("GBK");
    XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);
    writer.write(document);
    writer.close();

1.4.4. Pull解析

與Sax一樣.都屬于事件驅(qū)動的解析方式.
相比Sax解析過程更加靈活.
sax一旦開始解析就是從頭讀到尾.不解析完整個文檔不會停
pull解析較為靈活.是以事件為單位.手動向下繼續(xù). 如果獲得到我們要找的內(nèi)容. 可以停止繼續(xù)解析.

缺點:只能進行查詢,不能做增刪改

    public static List<Student> parseXML(InputStream is) throws Exception{
        //1:創(chuàng)建解析器工廠
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

        //2:使用工廠獲得解析器
        XmlPullParser parser = factory.newPullParser();

        //3: 使用解析器讀取XML流
        parser.setInput(is, "UTF-8");

        //4: 獲得當前事件的狀態(tài)
        int type = parser.getEventType();


        List<Student> list =null;
        Student stu = null;
        //5:判斷當前事件狀態(tài)
        while(type!=XmlPullParser.END_DOCUMENT){
            switch(type){
                case XmlPullParser.START_TAG:
                    if("students".equals(parser.getName())){
                        list = new ArrayList<Student>();

                    }else if("student".equals(parser.getName())){
                        stu = new Student();

                    }else if("name".equals(parser.getName())){
                        String name = parser.nextText();
                        stu.setName(name);
                    }else if("age".equals(parser.getName())){
                        int age = Integer.parseInt(parser.nextText());
                        stu.setAge(age);

                    }else if("height".equals(parser.getName())){
                        double height = Double.parseDouble(parser.nextText());
                        stu.setHeight(height);
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if("student".equals(parser.getName())){
                        list.add(stu);
                        stu = null;
                    }
                    break;
                default:
                    break;
            }
            //讓解析器向下解析一行,并返回改行的事件常量
            type = parser.next();
        }
        return list;
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容