XML學習筆記

什麼是XML

    XML全稱爲Extensible MarkupLanguage,意思是可擴展的標記語言。XML語法上和HTML比較相似,但HTML中的元素是固定的,而XML的標籤是可以由用戶自定義的。

    W3C在1998年2月發佈1.0版本,2004年2月又發佈1.1版本,但因爲1.1版本不能向下兼容1.0版本,所以1.1沒有人用。同時,在2004年2月W3C又發佈了1.0版本的第三版。我們要學習的還是1.0版本!!!


 應用常見

配置文件

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

<web-app version="2.5">

    <servlet>

        <servlet-name>HelloMyServlet</servlet-name>

        <servlet-class>cn.itcast.HelloMyServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>HelloMyServlet</servlet-name>

        <url-pattern>/hello</url-pattern>

    </servlet-mapping>

</web-app>


存放數據

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

<persons>

    <personid="p001">

        <name>張三</name>

    </person>

    <personid="p002">

        <name>李四</name>

    </person>

</persons>

 

XML 語法

XML文檔聲明

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

1.        文檔聲明必須爲<?xml開頭,以?>結束;

2.        文檔聲明必須從文檔的0行0列位置開始;

3.        文檔聲明只有三個屬性:

               versioin:指定XML文檔版本。必須屬性,因爲我們不會選擇1.1,只會選擇1.0;

               encoding:指定當前文檔的編碼。可選屬性,默認值是utf-8;

               standalone:指定文檔獨立性。可選屬性,默認值爲yes,表示當前文檔是獨立文檔。如果爲no表示當前文檔不是獨立的文檔,會依賴外部文件。

 

元素

<servlet>

1.        元素是XML文檔中最重要的組成部分,

2.        普通元素的結構開始標籤、元素體、結束標籤組成。例如:<hello>大家好</hello>

3.        元素體:元素體可以是元素,也可以是文本,例如:<b><a>你好</a></b>

4.        空元素:空元素只有開始標籤,而沒有結束標籤,但元素必須自己閉合,例如:<c/>

5.        元素命名:

              區分大小寫

             不能使用空格,不能使用冒號:

             不建議以XML、xml、Xml開頭

6.        良好的XML文檔,必須有一個根元素。

 

屬性

<web-app version="2.5">

1.        屬性是元素的一部分,它必須出現在元素的開始標籤中

2.        屬性的定義格式:屬性名=屬性值,其中屬性值必須使用單引或雙引

3.        一個元素可以有0~N個屬性,但一個元素中不能出現同名屬性

4.        屬性名不能使用空格、冒號等特殊字符,且必須以字母開頭

 

註釋

XML的註釋與HTML相同,即以「<!--」開始,以「-->」結束。註釋內容會被XML解析器忽略!

 

 轉義字符

XML中的轉義字符與HTML一樣。

因爲很多符號已經被XML文檔結構所使用,所以在元素體或屬性值中想使用這些符號就必須使用轉義字符,例如:「<」、「>」、「’」、「」」、「&」。

        

 

 CDATA區

<![CDATA[

    任意內容

]]>

當大量的轉義字符出現在xml文檔中時,會使xml文檔的可讀性大幅度降低。這時如果使用CDATA段就會好一些。

在CDATA段中出現的「<」、「>」、「」」、「’」、「&」,都無需使用轉義字符。這可以提高xml文檔的可讀性。

在CDATA段中不能包含「]]>」,即CDATA段的結束定界符。

 

DTD約束

什麼是DTD

DTD(Document TypeDefinition),文檔類型定義,用來約束XML文檔。規定XML文檔中元素的名稱,子元素的名稱及順序,元素的屬性等。

 

 DTD重點要求

開發中,我們很少自己編寫DTD約束文檔,通常情況我們都是通過框架提供的DTD約束文檔,編寫對應的XML文檔。常見框架使用DTD約束有:struts2、hibernate等。

 通過提供的DTD「web-app_2_3.dtd」編寫XML

  

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

<!--

    傳智播客DTD教學實例文檔。

    模擬servlet2.3規範,如果開發人員需要在xml使用當前DTD約束,必須包括DOCTYPE。

    格式如下:

    <!DOCTYPEweb-app SYSTEM "web-app_2_3.dtd">

-->

<!ELEMENT web-app (servlet*,servlet-mapping* ,welcome-file-list?) >

<!ELEMENT servlet(servlet-name,description?,(servlet-class|jsp-file))>

<!ELEMENT servlet-mapping(servlet-name,url-pattern) >

<!ELEMENT servlet-name (#PCDATA)>

<!ELEMENT servlet-class (#PCDATA)>

<!ELEMENT url-pattern (#PCDATA)>

 

<!ELEMENT welcome-file-list (welcome-file+)>

<!ELEMENT welcome-file (#PCDATA)>

 

<!ATTLIST web-app version CDATA #IMPLIED>


 DTD語法

 文檔聲明

1.        內部DTD,在XML文檔內部嵌入DTD,只對當前XML有效。

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

<!DOCTYPE web-app [

    ... //具體的語法

]>

<web-app>

</web-app>

2.        外部DTD—本地DTD,DTD文檔在本地系統上,公司內部自己項目使用。

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

<!DOCTYPE web-app SYSTEM"web-app_2_3.dtd">

<web-app>

</web-app>

3.        外部DTD—公共DTD,DTD文檔在網絡上,一般都有框架提供。

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

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems,Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

</web-app>

 

元素聲明

定義元素語法:<!ELEMENT元素名 元素描述>

    元素名:自定義

    元素描述包括:符號和數據類型

        常見符號:? * + () | ,

        常見類型:#PCDATA 表示內容是文檔,不能是子標籤

實例

<!ELEMENT web-app (servlet*,servlet-mapping* ,welcome-file-list?) >

    web-app 包括3個標籤,且必須順序出現。

        servlet子標籤個數任意

        servlet-mapping 子標籤個數任意

        welcome-file-list 子標籤最多隻能出現一次

<!ELEMENT servlet(servlet-name,description?,(servlet-class|jsp-file))>

    servlet 有3個子標籤,且必須順序出現

        servlet-name,必須有,且只能出現一次

        description,可選一次

        servlet-class 和 jsp-file 二選一,且只能出現一次

<!ELEMENT servlet-name (#PCDATA)>

    servlet-name的標籤體必須是文本

<!ELEMENT welcome-file-list (welcome-file+)>

    welcome-file-list至少有一個子標籤welcome-file

 

 屬性聲明

屬性的語法:

    <!ATTLIST元素名

        屬性名屬性類型 約束

        屬性名屬性類型 約束

        ...

    >

    元素名:屬性必須是給元素添加,所有必須先確定元素名

    屬性名:自定義

    屬性類型:ID、CDATA、枚舉 …

        ID : ID類型的屬性用來標識元素的唯一性

        CDATA:文本類型

        枚舉:(e1 | e2 | ...) 多選一

    約束:

        #REQUIRED:說明屬性是必須的;

        #IMPLIED:說明屬性是可選的;

 實例

<!ATTLIST web-app version CDATA #IMPLIED>

    給web-app元素添加 version屬性,屬性值必須是文本,且可選。

    <web-appversion="2.3"> 和 <web-app>都符號約束

 

Schema約束

什麼是Schema

Schema是新的XML文檔約束;

Schema要比DTD強大很多,是DTD 替代者;

Schema本身也是XML文檔,但Schema文檔的擴展名爲xsd,而不是xml。

Schema 功能更強大,數據類型更完善

Schema 支持名稱空間

 

Schema重點要求

與DTD一樣,要求可以通過schema約束文檔編寫xml文檔。常見框架使用schema的有:Spring等

         要求:可以通過提供「web-app_2_5.xsd」編寫xml文檔

        

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

<!--

    傳智播客Schema教學實例文檔。

    模擬servlet2.5規範,如果開發人員需要在xml使用當前Schema約束,必須包括指定命名空間。

    格式如下:

    <web-appxmlns="http://www.example.org/web-app_2_5"

            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

            xsi:schemaLocation="http://www.example.org/web-app_2_5web-app_2_5.xsd"

            version="2.5">

-->

<xsd:schemaxmlns="http://www.w3.org/2001/XMLSchema"

    targetNamespace="http://www.example.org/web-app_2_5"

    xmlns:xsd="http://www.w3.org/2001/XMLSchema"

    xmlns:tns="http://www.example.org/web-app_2_5"

    elementFormDefault="qualified">

   

    <xsd:elementname="web-app">

        <xsd:complexType>

            <xsd:choiceminOccurs="0" maxOccurs="unbounded">

                <xsd:element name="servlet">

                    <xsd:complexType>

                        <xsd:sequence>

                            <xsd:elementname="servlet-name"></xsd:element>

                            <xsd:elementname="servlet-class"></xsd:element>

                        </xsd:sequence>

                    </xsd:complexType>

                </xsd:element>

                <xsd:element name="servlet-mapping">

                    <xsd:complexType>

                        <xsd:sequence>

                            <xsd:elementname="servlet-name"></xsd:element>

                            <xsd:elementname="url-pattern"></xsd:element>

                        </xsd:sequence>

                    </xsd:complexType>

                </xsd:element>

                <xsd:elementname="welcome-file-list">

                    <xsd:complexType>

                        <xsd:sequence>

                            <xsd:elementname="welcome-file"maxOccurs="unbounded"></xsd:element>

                        </xsd:sequence>

                    </xsd:complexType>

                </xsd:element>

            </xsd:choice>

            <xsd:attributename="version" type="double"use="optional"></xsd:attribute>

        </xsd:complexType>

    </xsd:element>

</xsd:schema>


命名空間(語法)

什麼是命名空間

如果一個XML文檔中使用多個Schema文件,而這些Schema文件中定義了相同名稱的元素時就會出現名字衝突。這就像一個Java文件中使用了importjava.util.*和import java.sql.*時,在使用Date類時,那麼就不明確Date是哪個包下的Date了。

總之名稱空間就是用來處理元素和屬性的名稱衝突問題,與Java中的包是同一用途。如果每個元素和屬性都有自己的名稱空間,那麼就不會出現名字衝突問題,就像是每個類都有自己所在的包一樣,那麼類名就不會出現衝突。

 

約束文檔和XML關係

當W3C提出Schema約束規範時,就提供「官方約束文檔」。我們通過官方文檔,必須「自定義schema 約束文檔」,開發中「自定義文檔」由框架編寫者提供。我們提供「自定義文檔」限定,編寫出自己的xml文檔。


聲明命名空間

默認命名空間:<xxxxmlns=」」> ,使用<標籤>

顯式命名空間:<xxxxmlns:別名=」」> , 使用<別名:標籤>

實例:web-app_2_5.xsd

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"…>

    表示自定義schema約束文檔引用官方文檔作爲顯示命名空間。如果要使用官方提供的元素或屬性,必須使用xsd前綴(自定義,此處表示官方文檔,所以使用xsd)

    <xsd:schema>標籤就有官方文檔提供,默認命名空間直接使用。

實例:web.xml

<web-appxmlns=http://www.example.org/web-app_2_5 …>

    表示 xml 文檔引用「自定義約束文檔」作爲默認命名空間

    因爲使用默認命名空間,<web-app>直接使用

 

其他介紹

自定義約束:web-app_2_5.xsd

<xsd:schematargetNamespace=http://www.example.org/web-app_2_5

    表示給當前自定義約束文檔進行起名,提供給xml文檔使用。

 

 xml文檔:web.xml

<web-app

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation=http://www.example.org/web-app_2_5web-app_2_5.xsd

   

    xmlns:xsi=」…」固定寫法

        表示是一個schema實例文檔,就是被schema文檔約束的xml文檔。

    xsi:schemaLocation=」名稱 路徑  名稱路徑 名稱 路徑…」

        表示用於確定當前xml文檔使用到的schema文檔的位置。「名稱 路徑」是成對出現,與xmlns引用命名空間對應。


 dom4j解析

 XML解析概述

當將數據存儲在XML後,我們就希望通過程序獲得XML的內容。如果我們使用Java基礎所學習的IO知識是可以完成的,不過你需要非常繁瑣的操作纔可以完成,且開發中會遇到不同問題(只讀、讀寫)。人們爲不同問題提供不同的解析方式,並提交對應的解析器,方便開發人員操作XML。

 

解析方式和解析器

l  開發中比較常見的解析方式有三種,如下:

1.        DOM:要求解析器把整個XML文檔裝載到內存,並解析成一個Document對象。

                優點:元素與元素之間保留結構關係,故可以進行增刪改查操作。

                缺點:XML文檔過大,可能出現內存溢出顯現。

2.        SAX:是一種速度更快,更有效的方法。它逐行掃描文檔,一邊掃描一邊解析。並以事件驅動的方式進行具體解析,每執行一行,都將觸發對應的事件。(瞭解)

               優點:處理速度快,可以處理大文件

               缺點:只能讀,逐行後將釋放資源。

3.        PULL:Android內置的XML解析方式,類似SAX。(瞭解)


 解析器:就是根據不同的解析方式提供的具體實現。有的解析器操作過於繁瑣,爲了方便開發人員,有提供易於操作的解析開發包。

常見的解析開發包:

 JAXP:sun公司提供支持DOM和SAX開發包

 JDom:dom4j兄弟

 jsoup:一種處理HTML特定解析開發包

dom4j:比較常用的解析開發包,hibernate底層採用。

 

 DOM解析原理及結構模型

XML DOM 和 HTML DOM類似,XML DOM 將 整個XML文檔加載到內存,生成一個DOM樹,並獲得一個Document對象,通過Document對象就可以對DOM進行操作

DOM中的核心概念就是節點,在XML文檔中的元素、屬性、文本等,在DOM中都是節點!

 

API使用

如果需要使用dom4j,必須導入jar包。

dom4j 必須使用核心類SaxReader加載xml文檔獲得Document,通過Document對象獲得文檔的根元素,然後就可以操作了。

常用API如下:

1.        SaxReader對象

a)        read(…) 加載執行xml文檔

2.        Document對象

       getRootElement() 獲得根元素

3.        Element對象

       elements(…) 獲得指定名稱的所有子元素。可以不指定名稱

      element(…) 獲得指定名稱第一個子元素。可以不指定名稱

      getName() 獲得當前元素的元素名

      attributeValue(…) 獲得指定屬性名的屬性值

     elementText(…) 獲得指定名稱子元素的文本值

     getText() 獲得當前元素的文本內容

 

@Test

public void demo03() throws Exception{

    //#1 獲得document

    SAXReadersaxReader = new SAXReader();

    Documentdocument = saxReader.read(new File("src/cn/itcast/a_xml/web.xml"));

   

    //#2 獲得根元素

    ElementrootElement = document.getRootElement();

   

    //打印version屬性值

    Stringversion = rootElement.attributeValue("version");

    System.out.println(version);

   

    //#3 獲得所有子元素。例如:<servlet>/<servlet-mapping>

    List<Element>allChildElement = rootElement.elements();

   

    //#4 遍歷所有

    for(Element childElement : allChildElement) {

        // #5.1 打印元素名

        String childEleName =childElement.getName();

        System.out.println(childEleName);

       

        // #5.2 處理<servlet> ,並獲得子標籤的內容。例如:<servlet-name> 等

        if("servlet".equals(childEleName)){

            // 方式1:獲得元素對象,然後獲得文本

            ElementservletNameElement = childElement.element("servlet-name");

            StringservletName = servletNameElement.getText();

            System.out.println("\t"+ servletName);

           

            // 方式2:獲得元素文本值

            StringservletClass = childElement.elementText("servlet-class");

            System.out.println("\t"+ servletClass);

           

        }

       

        // #5.3 處理<servlet-mapping> 省略...

       

    }

   

}


案例實現

準備工作

爲了模擬服務器端程序,且可以同時存在多個類似程序。故提供接口,接口中有3個方法,我們人爲約定三個方法的調用順序。

public interface MyServlet {

   

    publicvoid init();     //1.初始化

   

    publicvoid service(); //2.執行

   

    publicvoid destory(); //3.銷燬

 

}

         在爲接口編寫實現類

                 

public class HelloMyServlet implements MyServlet {

 

    @Override

    publicvoid init() {

        System.out.println("1.初始化");

    }

 

    @Override

    publicvoid service() {

        System.out.println("2.執行中....");

    }

 

    @Override

    publicvoid destory() {

        System.out.println("3.銷燬");

    }

 

}

         測試,創建實現類實例對象

public class TestApp {

    @Test

    publicvoid demo01(){

        //手動創建執行

        MyServlet myServlet = new HelloMyServlet();

        myServlet.init();

        myServlet.service();

        myServlet.destory();

    }

}

 

反射執行

測試程序我們直接new HelloServlet,這種編程方式我們稱爲硬編碼,及代碼寫死了。爲了後期程序的可擴展,開發中通常使用實現類的全限定類名(cn.itcast.e_web.HelloMyServlet),通過反射加載字符串指定的類,並通過反射創建實例。

@Test

public void demo02() throws Exception{

    /* 反射創建執行

     * 1) Class.forName 返回指定接口或類的Class對象

     * 2) newInstance() 通過Class對象創建類的實例對象,相當於new Xxx();

     */

    StringservletClass = "cn.itcast.e_web.HelloMyServlet";

   

    //3 獲得字符串實現類實例

    Classclazz = Class.forName(servletClass);

    MyServletmyServlet = (MyServlet) clazz.newInstance();

    //4 執行對象的方法

    myServlet.init();

    myServlet.service();

    myServlet.destory();

}

 

解析xml

使用反射我們已經可以創建對象的實例,此時我們使用的全限定類名,在程序是仍寫死了,我們將器配置到xml文檔中。

         xml文檔內容:

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

<web-app version="2.5">

    <servlet>

        <servlet-name>HelloMyServlet</servlet-name>

        <servlet-class>cn.itcast.e_web.HelloMyServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>HelloMyServlet</servlet-name>

        <url-pattern>/hello</url-pattern>

    </servlet-mapping>

   

    <servlet>

        <servlet-name>HelloMyServlet2</servlet-name>

        <servlet-class>cn.itcast.e_web.HelloMyServlet2</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>HelloMyServlet2</servlet-name>

        <url-pattern>/hello2</url-pattern>

    </servlet-mapping>

</web-app>

         解析實現

@Test

public void demo03() throws Exception{

    /* 讀取xml配置文件,獲得<servlet-class>配置的內容,取代固定字符串

     */

   

    //1.1 加載xml配置文件,並獲得document對象

    SAXReadersaxReader = new SAXReader();

    Documentdocument = saxReader.read(new File("src/cn/itcast/e_web/web.xml"));

    //1.2 獲得根元素

    ElementrootElement = document.getRootElement();

    //1.3 獲得第一個<servlet> 子元素

    ElementservletElement = rootElement.element("servlet");

    //1.4 獲得字符串實現類 <servlet-class>的值

    StringservletClass = servletElement.elementText("servlet-class");

   

    //3 獲得字符串實現類實例

    Classclazz = Class.forName(servletClass);

    MyServletmyServlet = (MyServlet) clazz.newInstance();

    //4 執行對象的方法

    myServlet.init();

    myServlet.service();

    myServlet.destory();

}

 

模擬瀏覽器路徑

上面我們已經解析xml,不過我們獲得內容是固定。我們希望如果用戶訪問的路徑是/hello,將執行cn.itcast.e_web.HelloMyServlet程序,如果訪問時/hello2,將執行cn.itcast.e_web.HelloMyServlet2程序。

         在執行測試程序前(@Before),解析xml文件,將解析的結果存放在Map中,map中數據的格式爲:路徑=實現類。

        

         解析xml思路:先解析<servlet>,將結果存放map,name=class,然後再解析<servlet-mapping>通過name獲得class,再將url=class存放到map,最後將name=class移除。

//最終存放key=請求路徑,value=實現類

private Map<String, String> data = newHashMap<String,String>();

@Before

public void demo04Before() throws Exception{

    //在執行前執行,解析xml,並將結果存放到Map<路徑,實現類>中

    //1 獲得document

    SAXReadersaxReader = new SAXReader();

    Documentdocument = saxReader.read(new File("src/cn/itcast/e_web/web.xml"));

    //2 獲得根元素

    ElementrootElement = document.getRootElement();

    //3 獲得所有的子元素 <servlet> 、<servlet-mapping>等

    List<Element>allChildElement = rootElement.elements();

    /* 4 遍歷所有

     * 1)解析到<servlet>,將其子標籤<servlet-name>與<servlet-class>存放到Map中

     * 2)解析到<servlet-mapping>,獲得子標籤<servlet-name>和<url-pattern>,從map中獲得1的內容,組合成 url = class 鍵值對

     */

    for(Element childElement : allChildElement) {

        //4.1 獲得元素名

        String eleName = childElement.getName();

        //4.2 如果是servlet,將解析內容存放到Map中

        if("servlet".equals(eleName)){

            StringservletName = childElement.elementText("servlet-name");

            StringservletClass = childElement.elementText("servlet-class");

            data.put(servletName,servletClass);

        }

        //4.3 如果是servlet-mapping,獲得之前內容,組成成key=url,value=class並添加到Map中

        if("servlet-mapping".equals(eleName)){

            StringservletName = childElement.elementText("servlet-name");

            StringurlPattern = childElement.elementText("url-pattern");

            // 獲得<servlet-name>之前存放在Map中<servlet-class>值

            StringservletClass= data.get(servletName);

            // 存放新的內容 url = class

            data.put(urlPattern,servletClass);

            // 將之前存放的數據刪除

            data.remove(servletName);

        }

       

        //打印信息

        System.out.println(data);

       

    }

}

         模擬瀏覽器請求路徑,通過url從map獲得class,並使用反射執行實現類。

@Test

public void demo04() throws Exception{

   

    //1 模擬路徑

    String url= "/hello";

//      String url = "/hello2";

   

    //2 通過路徑獲得對應的實現類

    StringservletClass = data.get(url);

   

    //3 獲得字符串實現類實例

    Classclazz = Class.forName(servletClass);

    MyServletmyServlet = (MyServlet) clazz.newInstance();

    //4 執行對象的方法

    myServlet.init();

    myServlet.service();

    myServlet.destory();

}

 

瀏覽器訪問(瞭解)

使用Socket編寫服務,通過瀏覽器可以訪問,並解析瀏覽器發送的請求數據,最終獲得請求路徑。

訪問路徑:http://localhost:8888/hello

@Test

public void demo05() throws Exception{

    //使用socket獲得請求路徑

   

    //1.1 給本地計算機綁定端口8888

    ServerSocketserverSocket = new ServerSocket(8888);

    //1.2 程序阻塞,等待瀏覽器請求。

    Socketaccept = serverSocket.accept();

    //1.3 獲得請求所有數據

    BufferedReaderreader = new BufferedReader(new InputStreamReader(accept.getInputStream()));

    //1.4 獲得第一行數據,請求行,例如:GET /hello HTTP/1.1

    StringfirstLine = reader.readLine();

    //1.5 請求行三部分數據由空格連接,獲得中間數據。表示請求路徑

    String url= firstLine.split(" ")[1];

    System.out.println(url);

   

    //2 通過路徑獲得對應的實現類

    StringservletClass = data.get(url);

   

    //3 獲得字符串實現類實例

    Classclazz = Class.forName(servletClass);

    MyServletmyServlet = (MyServlet) clazz.newInstance();

    //4 執行對象的方法

    myServlet.init();

    myServlet.service();

    myServlet.destory();

    //5 釋放資源

    reader.close();

    client.close();

}

 顯示效果

控制檯顯示效果

幾秒之後,服務程序沒有做出任何響應,瀏覽器將顯示「無法訪問」