09- XML 語法基礎與解析技術

XML 技術

XML 的概念

可擴展標記語言,標準通用標記語言的子集,是一種用於標記電子文件使其具有結構性的標記語言。

XML 和 HTML的關係

聯繫:
     1、從外觀上看,名字裏都有一個“ML”,就是標記語言的意思,這不得不說是一個相似點吧?
     不錯,它們在名字上相似,在結構上也非常像,事實上XML是對HTML的補充。
     2、轉換:爲了更好地適應client瀏覽器的瀏覽,能夠在server端進行XML->HTML的轉換,但普通靜態控件不支持這樣的轉換,能夠選擇支持XMLDOM的ASP或PHPserver進行。
     3、操作性:它們自身都不進行操作,XML描寫敘述數據,HTML顯示。
    
差別:
     1、目的:XML是用來描寫敘述、傳輸和存儲數據的,即,關於什麼是數據及怎樣存放的問題,焦點在數據的內容;
         HTML是用來顯示數據的,即,關於怎樣顯示及較好地顯示的問題,焦點在數據的外觀。
     2、語法:完整性方面,XML要求全部標記都要成對出現,HTML顯然不是,比方img、input等,能夠無結束標籤;
         大寫和小寫方面,HTML不區分大寫和小寫,而XML嚴格區分。
     3、自主性:即能否夠自定義。XML標記由架構或文檔作者自定義,沒有什麼限制;HTML則不行,僅僅能用提前定義的,即head、body等。

xml 文檔結構

聲明 + 根標籤 (子標籤)

有且僅有有一個根標籤

<?xml version="1.0" encoding="UTF-8" ?>
<books>
    <book>
        <author></author>
        <title></title>
        <description></description>
    </book>
    <book>
        <author></author>
        <title></title>
        <description></description>
    </book>
</books>

標籤可以自定義添加

書中 有 作者, 標題, 描述, 頁數, 出版社, 出版日期, 價格

自定義案例

描述一個人的午餐需求

<?xml version="1.0" encoding="UTF-8" ?>
<午餐>
    <主食>意大利麪</主食>
    <菜品>
        <菜品1>法式焗蝸牛</菜品1>
        <菜品2>法國鵝肝</菜品2>
        <菜品3>頂配魚子醬</菜品3>
        <菜品4>神戶雪花牛肉</菜品4>
    </菜品>
    <湯>松茸南瓜湯</湯>
    <甜點>提拉米蘇</甜點>
</午餐>

標籤語法標準

標籤元素

屬性

標籤組成 <標籤名 屬性= “值”>內容</標籤名>

語法要求

標籤名  必須使用<></>  必須成對出現 (有開始有結束)
標籤名 大小寫敏感(區分大小寫)
元素名稱不能以數字或者標點符號開始 也不能包含空格或冒號
XML 不能交叉嵌套
屬性	屬性值需要使用 雙引號 包裹
	一個元素可以有多個屬性
	屬性值不能包含特殊字符(  <  "  & )

軟性要求

良好的縮進習慣

特殊字符

符號 表示方式
< &lt(😉
> &gt
& &amp
" &quot
&apos

衣服尺碼案例

<?xml version="1.0" encoding="UTF-8" ?>
<size>
    <S>身高&lt;165</S>
    <M>165&lt;身高&lt;170</M>
    <L>170&lt;身高&lt;175</L>
    <XL>175&lt;身高&lt;180</XL>
    <XXL>180&lt;身高&lt;185</XXL>
</size>

註釋

<!-- 註釋內容 -->

xml優勢

  • 數據存儲 (保存衣服尺碼, 學生分數, 書籍信息)
  • 數據交換
  • 數據配置 (因爲可以保存各種格式的信息, 很多軟件使用xml 保存軟件的配置信息)

命名空間

在XML 中 用作元素或屬性名稱的名稱集合, 用來標識來自特定域的名稱

問題 小米 是什麼意思
    小米是一種糧食
    小米是一款手機
問題 table 是什麼意思
	一張桌子
	一張表格
同一個詞在不同的場景下有不同的含義
我們爲了區分不同的場景, 引入了命名空間
	地裏面長出來的小米 是什麼意思
	科技公司小米公司中的小米 是什麼意思
	英語單詞中 table 是什麼意思
	HTML 語言中 table 是什麼意思

必要性

XML 解析器在解析XML 文檔的時候, 對於重名的元素, 可能出現解析衝突, 命名空間有助於標準化元素和屬性, 爲它們添加唯一的標識

聲明語法

ns namespace

<根標籤  xmlns:命名空間=命名空間的地址信息> 

案例

<?xml version="1.0" encoding="UTF-8" ?>
<students
    xmlns:middle="http://www.middle.com"
    xmlns:university="http://www.university.com">
    <middle:stu>
        <name>三毛</name>
        <age>13</age>
    </middle:stu>
    <university:stu>
        <name>大毛</name>
        <age>22</age>
    </university:stu>
</students>

屬性也可以使用命名空間

 <stu name="張三" nick:name="三"></stu>

XML 約束

XML 因爲是可擴展的, 不能無限制的書寫, 需要通過約束加以規範

DTD 約束

簡介

Document Type Definition 文檔類型定義
DTD是 xml文檔 對自身格式的一個描述, 可以使用DTD校驗文檔數據的有效性

作用

規則

1- 元素的定義規則
2- 元素之間的關係規則
3- 屬性的定義規則

語法

聲明

<!DOCTYPE 根元素 [定義的內容]>

元素

<!ELEMENT 標籤名 元素類型> 

屬性

<!ATTLIST 元素名
    屬性名1 屬性類型 設置說明
    屬性名2 屬性類型 設置說明
...>

屬性類型

CDATA:屬性值爲任意文本數據;
Enumerated:屬性值必須是枚舉列表中的一個;
ID:屬性值必須是唯一的,並且屬性值不能以數字開頭;

設置說明

1:#REQUIRED,表示屬性是必須的。
2:#IMPLIED,表示屬性是可選的。

實體

<!ENTITY CCTV "中國中央電視臺">
必須使用內部引入的方式, 才能夠正常顯示
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE poems SYSTEM "poems.dtd"[
        <!ENTITY CCTV "中國中央電視臺">]>
<poems>
    <poem time="唐代" type="五言律詩">
        <author>王維</author>
        <title>鹿柴</title>
        <content>&CCTV;</content>
    </poem>
</poems>

案例

案例代碼

<?xml version="1.0" encoding="UTF-8" ?>
<poems>
    <poem>
        <author>王維</author>
        <title>鹿柴</title>
        <content>空山不見人,但聞人語響.</content>
        <page>500</page>
    </poem>
</poems>

約束代碼

<!DOCTYPE poems [
        <!ELEMENT poems (poem) >
        <!ELEMENT poem (author,title,content,page)>
        <!ELEMENT author (#PCDATA)>
        <!ELEMENT title (#PCDATA)>
        <!ELEMENT content (#PCDATA)>
        <!ELEMENT page (#PCDATA)>
]>

DTD約束中的符號

符號 含義 實例 實例說明
() 用來給元素分組 (author,title,content,page) 元素中的子元素組
| 從列出的對象中
選擇其中之一
(author|title) author 或者 title 其中之一
, 對象按照指定順序
全部出現
(author,title) 必須全部按照順序出現
* 該對象允許
零次或多次
(author*,title) author標籤可以不出現,也可以出現多次
? 該對象允許
零次或一次
(author?,title) author標籤可以不出現,也可以最多出現1次
+ 該對象允許
一次或多次
(author+,title) author標籤最少出現1次

添加屬性約束

<?xml version="1.0" encoding="UTF-8" ?>
<poems>
    <poem time="唐代" type="五言律詩">
        <author>王維</author>
        <title>鹿柴</title>
        <content>空山不見人,但聞人語響.</content>
        <page>500</page>
    </poem>
</poems>

添加屬性約束

<!ATTLIST poem
        time CDATA #REQUIRED
        type CDATA #IMPLIED>

引用方式

內部定義

外部定義

1- 在外部定義一個後綴名爲dtd 的文件
2- 把約束內容複製到 dtd文件中
3- 在xml 中 引入 dtd 約束文件
<!DOCTYPE poems SYSTEM "poems.dtd">
<!DOCTYPE 根標籤 SYSTEM "文件路徑">

外部引入網絡文件

<!DOCTYPE 根標籤 PUBLIC "文件路徑">

Schema 約束

Schema 約束的概念

用一套 預先規定的XML 元素和屬性創建的約束, 這些元素和屬性, 定義了XML 穩當的結構和內容模式

相比於DTD的優勢

Schema 相對於DTD約束 
- 更加豐富的數據類型
- 更加友好的 約束格式 (約束文檔格式也是 XML 的格式 , 內行人領導內行人)
- Schema 支持命名空間
- 定義約束的能力非常強大,可以對XML實例文檔作出細緻的語義限制。 

語法格式

文檔後綴名 .xsd

是完全符合 XML 語法的

根元素 爲 schema

命名空間

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

案例代碼

xml 文件

<customer>
    <name>張三</name>
    <address>XXX省XX市 ... </address>
</customer>

xsd文件格式

?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://mynamespace/mynamespace"
            elementFormDefault="qualified">
    <xsd:element name="customer">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="name" type="xsd:string"/>
                <xsd:element name="address" type="xsd:string" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

解析

xsd 文件中 
1- xml:xsd   該約束文件所依賴約束位置
該文件鎖需要的元素和數據類型, 是來自於 http://www.w3.org/2001/XMLSchema 命名空間
2- targetNamespace  該約束文件所定義的 元素和數據類型 會添加到該命名空間
	xml 文件 如果想要受到該文件約束, 就需要使用 命名空間引入 該空間
3- elementFormDefault=unqualified 目標命名空間不一定必須遵循該Schema 約束 
	(必須循序使用 elementFormDefault=qualified)

xml引入 xsd約束文件的方式

本地引入

<customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="customer.xsd">
    <name>張三</name>
    <address>XXX省XX市 ... </address>
</customer>

命名空間引入

<customer xmlns="http://mynamespace/mynamespace"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://mynamespace/mynamespace customer.xsd">
    <name>張三</name>
    <address>XXX省XX市 ... </address>
</customer>

解析

1-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance 
表示 該xml文件是一個被約束的文件
2-  xsi:schemaLocation 一般的值爲 targetNameSpace 空格 約束文檔的地址路徑(名稱)

Schema 元素類型

根元素

schema 
xsd 文件中 
1- xml:xsd   該約束文件所依賴約束位置
該文件鎖需要的元素和數據類型, 是來自於 http://www.w3.org/2001/XMLSchema 命名空間
2- targetNamespace  該約束文件所定義的 元素和數據類型 會添加到該命名空間
	xml 文件 如果想要受到該文件約束, 就需要使用 命名空間引入 該空間
3- elementFormDefault=unqualified 目標命名空間不一定必須遵循該Schema 約束 
	(必須循序使用 elementFormDefault=qualified)

element 元素

用於聲明一個標籤(元素) 

attribute 元素

定義一個屬性

簡單類型 simpleType

複雜類型 complexType

1- 序列 sequence
2- 選擇 choice

XML 解析技術

之前的學習, 是XML 如何存儲數據的 , 解析技術 是爲了從格式良好的XML 文檔中獲取數據

解析技術分類

DOM

Document Object Model 文檔對象模型

文檔樹

把一個格式良好的 xml文檔 看做是一顆節點數, 根節點爲樹根, 每一個子節點都是一條樹枝

DOM

基於XML文檔樹結構的解析
適用於多次訪問的XML文檔
特點:比較消耗資源

JDOM

DOM4J

DOM for java 
非常優秀的Java XML API
性能優異、功能強大
開放源代碼

SAX

基於事件的解析
適用於大數據量的XML文檔
特點:佔用資源少,內存消耗小

SAX 和 DOM 的對比

DOM 把整個文檔全部加載到內存中 , 根據指定的方法獲取文檔樹的節點
SAX 一行一行的執行, 當執行到某些具體的,帶有某些標誌性的行的時候, 執行相應的事件

案例

一麻袋硬幣
數一數一共有多少枚硬幣
1- 先稱出 一個硬幣的重量, 再稱所有硬幣的重量 , 兩者相除, 所得即爲硬幣的數量
	(效率很高, 佔用內存大, 需要有人提起所有硬幣秤一下)
2- 也可以一個一個數數量
	(效率較低, 用時較長, 但是佔用內存小, 查的人每一次手裏只需要握有一枚硬幣)

在這裏插入圖片描述

使用DOM 解析 XML

案例準備

<?xml version="1.0" encoding="UTF-8" ?>
<PhoneInfo>
    <Brand name="華爲">
        <Type name="U8650"></Type>
    </Brand>
    <Brand name="蘋果">
        <Type name="iPhone4"></Type>
        <Type name="iPhone5"></Type>
    </Brand>
    <Brand name="小米">
        <Type name="note 7 pro"></Type>
        <Type name="小米9"></Type>
    </Brand>
</PhoneInfo>

步驟分析

1- 創建DOM對象 (解析器)
2- 通過解析器對象, 解析整個XML文檔, 獲取整個文檔的 DOM樹對象
3- 通過對 DOM樹對象, 對整個XML 文檔 完成增刪改查操作

關鍵代碼

package s0809;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

public class Demo1_Dom {

    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        // 創建DOM解析器
        // 1- 先創建一個工廠
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        // 2- 通過工廠生成解析器
        DocumentBuilder db = dbf.newDocumentBuilder();
        // 通過解析器加載 xml 文檔
        Document doc = db.parse("src\\s0809\\phone.xml");
        // 通過DOM 樹 完成 對XML 文檔的增刪改查操作
        // 1- 查詢操作
        // 根據標籤名 獲取 節點對象
        NodeList brandList = doc.getElementsByTagName("Brand");
        // 循環遍歷獲取每一個 brand 的內容
        for(int i = 0 ; i < brandList.getLength(); i++){
            //NodeList.item(i) 相當於 List.get(i)
            Node brand = brandList.item(i);
            // 節點 包括(元素節點  文本節點 註釋節點 等等 )
            // 把節點轉換爲 標籤對象
            Element ele = (Element) brand;
            // 獲取該標籤的屬性
            String name = ele.getAttribute("name");
            System.out.println(name);
            // 獲取每一個品牌的 所有子元素標籤
            NodeList typeList = ele.getChildNodes();
            for(int k = 0 ; k < typeList.getLength(); k++){
                // 遍歷獲取每一個 type 標籤
                Node type = typeList.item(k);
                if(type instanceof Element){
                    Element typeEle = (Element)type;
                    String typeName = typeEle.getAttribute("name");
                    System.out.println(name+"--"+typeName);
                }
            }
        }

    }
}

Document常用方法

getElementsByTagName("Brand")   根據標籤名獲取該標籤對象的集合
doc.createElement()		創建元素對象
doc.createAttribute()	創建屬性對象

Element 標籤對象的常用方法

Element 方法 描述說明
ele.getTagName()
ele.getNodeValue()
ele.getAttribute(“name”) 根據屬性名稱獲取屬性值
ele.setAttribute();
ele.hasAttribute()
ele.removeAttribute();
ele.getChildNodes() 獲取所有子節點對象
ele.appendChild()
ele.removeChild()
ele.replaceChild()

給手機收藏添加新手機

//  添加 oppo 手機信息    Reno 
<Brand name = "oppo">
	<type name="Reno">

案例代碼

提取公共方法

public static Document getDocument()throws Exception{
    // 創建DOM解析器
    // 1- 先創建一個工廠
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    // 2- 通過工廠生成解析器
    DocumentBuilder db = dbf.newDocumentBuilder();
    // 通過解析器加載 xml 文檔
    Document doc = db.parse("src\\s0809\\phone.xml");
    return doc;
}

測試添加方法

//添加 元素
public static void testAdd()throws  Exception{
    // 獲取Doc 對象
    Document doc = getDocument();
    // <Brand name = "oppo">
    Element brand = doc.createElement("Brand");
    brand.setAttribute("name","Oppo");
    // 給手機品牌設置 子標籤 Type
    //<type name="Reno">
    Element type = doc.createElement("Type");
    type.setAttribute("name","Reno");

    // 把Type 標籤 添加到 brand 中
    brand.appendChild(type);
    // 把 brand 添加到 根標籤中
    // 獲取根標籤
    NodeList phoneInfo = doc.getElementsByTagName("PhoneInfo");
    Element info = (Element) phoneInfo.item(0);
    info.appendChild(brand);
    // 僅僅是在內存中 包含關係已經確定了, 如何把內存中的內容 持久化到硬盤文件中

    // 持久化流程
    // 1- 創建持久化對象工廠
    TransformerFactory tf = TransformerFactory.newInstance();
    // 2- 創建持久化對象
    Transformer transformer = tf.newTransformer();
    // 把Document對象 包裝成  domSource 對象
    DOMSource domSource = new DOMSource(doc);
    // 設置編碼格式
    transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
    // s設置輸出流資源
    StreamResult result = new StreamResult(new FileOutputStream("src\\s0809\\phone.xml"));
    transformer.transform(domSource,result);

}

DOM4J 解析XML

DOM4J 不屬於 java 內置的 程序包, 屬於第三方 (Junit 需要引入第三方Jar 包)

查詢

使用DOM4J 讀取 Phone.xml的內容

// 遍歷phone.xml 中的信息
public static void seachAll() throws DocumentException {
    // 新建解析器對象
    SAXReader reader = new SAXReader();
    // 獲取DOM 文檔對象
    Document read = reader.read("src\\s0809\\phone.xml");
    // 使用DOM4J解析 必須先獲取根節點
    Element root = read.getRootElement();
    //根據根節點 獲取所有子節點
    List<Element> brandList = root.elements();
    // 通過 for each 循環 遍歷子節點
    for(Element brand : brandList){
        // 獲取 節點名稱
        // System.out.println(brand.getName());
        // 獲取該節點的 屬性對象
        Attribute brandAttr = brand.attribute("name");
        System.out.println(brandAttr.getValue());
        // 獲取品牌之下的 子節點 型號標籤
        List<Element> types = brand.elements();
        for(Element type : types){
            Attribute typeAttr = type.attribute("name");
            System.out.println("\t"+typeAttr.getValue());
        }
    }
}

新增節點

public static void  add()throws Exception{
    // 新建解析器對象
    SAXReader reader = new SAXReader();
    // 獲取DOM 文檔對象
    Document read = reader.read("src\\s0809\\phone.xml");
    // 使用DOM4J解析 必須先獲取根節點
    Element root = read.getRootElement();

    // 給 根節點 添加一個子節點 Brand
    Element brand = root.addElement("Brand");
    brand.addAttribute("name","VIVO");
    Element type = brand.addElement("Type");
    type.addAttribute("name","X23");


    // 設置編碼格式
    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding("UTF-8");
    // 把DOM 持久化到文件中
    XMLWriter writer = new XMLWriter(new FileWriter("src\\s0809\\phone.xml"),format);
    writer.write(read);
    writer.close();

}

思考 把XML 轉換爲 對象

新建一個Phone 類

有name 屬性

思考 如何 解析 XML 的同時 創建 Phone 的對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章