天天看點

javaweb 之 XML總結(一)XML

XML

一、簡介

XML 指可擴充标記語言(EXtensible Markup Language)。

XML 是一種标記語言,很類似 HTML,也是使用标簽來操作。

可擴充:HTML裡面的标簽是固定的,每個标簽都有特定的含義。而XML裡面的标簽可以自己定義,甚至可以寫中文的标簽。

XML的用途:HTML是用于顯示資料的,XML也是可以顯示資料的,但它不是XML的主要功能,XML 的設計宗旨是傳輸資料,而非顯示資料。

XML是兩個版本,1.0和1.1。我們一般使用1.0版本,因為1.1版本不能向下相容。

二、XML的應用

  • 不同系統之間傳輸資料

    最早的時候,消息發送資訊的格式為字元串,比如:

    String str = "zhangsan:lisi:hello:2019-4-19";

    字元串中我們用

    :

    作為分隔,每一段代表一個資訊。 但是這樣的代碼,别人很難明白其中的含義,閱讀性很差。 而且如果想擴充一下,添加一些新的資訊,就會很困難。是以這種格式可維護性很差。

    現在我們使用的新的方式來表示資訊:

    String str = "
    <message id='1'>
    	<sender>zhangsan</sender>
    	<getter>lisi</getter>
    	<content>hello</content>
    	<date>2019-4-19</date>
    </message>
    ";
               
    這樣的話,可閱讀性大大提高,而且擴充起來很友善,大大提高了程式的可維護性。
  • 用來表示生活中有關系的資料

    生活中的事物之間存在大量的關系,我們可以使用xml來表示這些有關系的資料。

    例如:

    javaweb 之 XML總結(一)XML
  • 經常用在配置檔案

    例如在java連接配接資料庫的時候,需要資料庫的URL、使用者名和密碼,我們把它存在xml中,如果想要修改,就不許需要修改源代碼,便于修改和維護。

三、xml的文法

xml的文檔說明

  • 建立一個檔案,字尾名是 .xml 。
  • 如果寫xml,第一步必須要有一個文檔說明

    <?xml version="1.0" encoding="gbk"?>

  • 文檔說明必須寫在第一行第一列
  • 屬性:

    version:xml的版本

    encoding:xml編碼、gbk、utf-8、iso8859-1(不包含中文)

    standalone:是否需要依賴其他的檔案 yes/no

如果 xml中文亂碼問題,在儲存 xml檔案的時候把編碼設定成跟文檔聲明一樣的類型。

定義元素(标簽)

标簽的定義:

  • 标簽定義必須有開始有結束:

    <person> </person>

  • 标簽沒有内容,可以在标簽内結束:

    <aa/>

  • 标簽可以嵌套,但是必須合理嵌套

    合理嵌套:

    <aa> <bb></bb> </aa>

    不合理嵌套:

    <aa> <bb></aa> </bb>

    不正确!
  • 一個xml中隻能有一個根标簽!其他的标簽都是這個标簽下面的标簽。
  • 注意:在xml中,空格和換行都當成内容來解析,下面這兩段代碼的含義是不同的。
    <aa>
    	hello
    </aa>
               
  • xml中标簽的命名規則
    1. xml代碼區分大小寫,

      <p>

      <P>

      這兩個标簽是不同的。
    2. 标簽名稱不能以

      數字

      或者

      下劃線

      開頭。

      <2a>

      <_aa>

      都不可允許。
    3. 不能以

      xml

      XML

      Xml

      等開頭。
    4. 标簽裡面不能包含空格和冒号。

      a b

      b:c

      都是不允許的。
    5. 标簽可以使用中文,但是不建議這樣做。

定義屬性

html是标記型文檔,可以有屬性。

xml也是标記型文檔,也可以有屬性。

屬性定義的要求:

  1. 一個标簽上可以有多個屬性

    <person id1="aaa" id2="bbb"></person>

  2. 屬性名稱不能相同。
  3. 屬性名稱與屬性值之間用

    =

    連接配接,屬性值要用引号括起來,可以是單引号,可以是雙引号。
  4. 屬性的命名規範和元素的命名規範是一緻的。

注釋

寫法:

<!-- xml的注釋 -->

注意:注釋是不能有嵌套的。

注釋也不能放到第一行!

特殊字元

<a>a<b</a>

如果想在xml中顯示 a<b ,是不能正常顯示的,因為把<當成标簽。

如果想要顯示,就需要使用轉義字元。

常用的轉義字元:

&lt;

< 小于

&gt;

> 大于

&amp;

& 和号

&apos;

’ 單引号

&quot;

" 引号

CDATA區

如果我們想要輸入:

我們知道是顯示不出來的,因為裡面的小于号都需要轉義。

而CDATA區可以解決多個字元都需要的轉義的操作。

寫法:

<![CDATA[ 内容 ]]>

上面的我們要修改為:

<![CDATA[ <aa>if(a<b && b<c && c<d){}</aa> ]]>
           

這樣把特殊字元當做文本内容,而不是标簽。

PI指令(處理指令)

可以在xml中設定樣式。可以通過PI指令來引入外部css檔案。

寫法:

<?xml-stylesheet type="text/css" href="xxx.css" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" ?>

示例:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="xxx.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" ?>
<person>
	<name>zhangsan</name>
	<age>20</age>
</person>
           

注意:設定樣式,隻對于英文名稱的标簽起作用,對于中文名稱的标簽是不起作用的。

四、xml的限制

  • 為什麼需要限制?
    <?xml version="1.0" encoding="utf-8"?>
    <person>
    	<name>zhangsan</name>
    	<age>20</age>
    </person>
               
    比如我們現在定義一個person的xml檔案,隻想要在裡面儲存人的一些資訊,比如name、age等,但是如果在xml檔案中寫入了一個

    <貓>

    的标簽,發現它是可以正常顯示的,因為符合文法規範。但是貓肯定不是人的資訊,是以我們這個時候就需要一種技術來規定xml中隻能出現的元素,這就叫xml的限制。
  • xml中限制的技術:dtd限制和schema限制

dtd限制

我們建立一個檔案,字尾名是

.dtd

步驟:

  1. 看xml中有多少元素,有幾個元素,在dtd檔案中寫幾個

    <!ELEMENT>

  2. 判斷這個元素是簡單元素還是複雜元素。

    複雜元素: 有子元素的元素

    格式:

    <!ELEMENT 元素名稱 (子元素)>

    例如上面的person:

    <!ELEMENT person (name,age)>

    簡單元素: 沒有子元素

    格式:

    <!ELEMENT 元素名稱 (#PCDATA)>

    例如上面的age、name:

    <!ELEMENT name (#PCDATA)>

    <!ELEMENT age (#PCDATA)>

  3. 需要在xml檔案中引入dtd檔案。

    <!DOCTYPE 根元素名稱 SYSTEM "dtd檔案的路徑">

如果使用浏覽器打開 xml檔案的話,浏覽器隻負責校驗 xml的文法,不負責校驗 xml的限制。

如果想要校驗 xml的限制,需要使用工具,工具有很多種,例如 MyEclipse。

javaweb 之 XML總結(一)XML

dtd的三種引入方式

  • 引入外部的dtd檔案

    <!DOCTYPE 根元素 SYSTEM "dtd路徑">

  • 内部的dtd
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE person [
    	<!ELEMENT person (name,age)>
    	<!ELEMENT name (#PCDATA)>
    	<!ELEMENT age (#PCDATA)>
    ]>
    <person>
    	<name>zhangsan</name>
    	<age>20</age>
    </person>
               
  • 使用外部的dtd檔案(網絡上的dtd檔案)
    <!DOCTYPE 根元素 PUBLIC "DTD名稱" "dtd文檔的URL">
               
    在structs2 使用配置檔案 使用 外部的dtd檔案,需要用到這個知識。

dtd定義元素

文法:

<!ELEMENT 元素名稱 限制>

  • 簡單元素:

    <!ELEMENT name (#PCDATA)>

    (#PCDATA)

    :限制name是字元串類型

    EMPTY

    :元素為空,不能有内容。

    ANY

    :任意,可以為空,也可以不為空
  • 複雜元素:

    <!ELEMENT 元素名稱 (子元素)>

    例如:

    <!ELEMENT person (name,age)>

    這樣的格式下,name和age子元素隻能出現一次

    設定子元素出現的次數:

    +

    :出現最少一次,一次或者多次

    ?

    :最多出現一次,零次或者一次

    *

    :任意次數都可以,零次、一次、多次都可以

    示例:

    <!ELEMENT person (name+,age?)>

    子元素用

    ,

    隔開,也表示元素出現的順序,必須按照先後順序出現。

    子元素用

    |

    隔開,表示隻能出現其中的任意一個。

dtd定義屬性

使用dtd限制标簽上的屬性,文法:

<!ATTLIST 元素名稱
	屬性名稱 屬性類型 屬性的限制
>
           

屬性類型

  • 字元串類型:CDATA
    <!ATTLIST person
    	name CDATA #REQUIRED
    >
               
  • 枚舉類型:表示一定範圍内隻能出現其中一個。
    <!ATTLIST person
    	name (AA|BB|CC) #REQUIRED
    >
               
  • ID 表示屬性的取值不能重複,而且必須以字母或者下劃線開頭。
    <!ATTLIST person
    	name ID #REQUIRED
    >
               

    屬性的限制

    #REQUIRED:屬性必須存在

    #REQUIRED

    #IMPLIED:屬性可有可無

    #IMPLIED

    #FIXED:表示一個固定值

    #FIXED "AAA"

    直接值:表示屬性預設值,當在标簽上不寫這個屬性的時候,就使用直接值,如果寫了屬性,就使用寫的值。

dtd定義引用實體

概念:在dtd中定義,在xml中使用。

文法:

<!ENTITY 實體名稱 "實體内容">

引用方式:

&實體名稱;

例如:

dtd中定義:

<!ENTITY copyright "veeja版權所有">

xml中引用:

&copyright;

定義的實體需要使用在内部的dtd中,如果寫在外部的dtd中,在某些版本的浏覽器,是不能正常使用的。

五、XML如何解析

DOM和SAX

xml有兩種解析方式:dom和sax。

  • DOM解析XML:

    與DOM解析HTML類似的是,dom解析的時候也會根據xml的層級結構,把xml中的每部分封裝成一個對象,在記憶體中配置設定一個樹形結構。

    DOM的優點在于可以很友善的實作增删改的操作。

    DOM的缺點在于解析xml檔案的時候,如果xml檔案過大,就會導緻記憶體溢出。

  • sax解析xml:

    sax解析采用事件驅動,一邊讀取一邊解析,解析到某一個對象,把對象的名稱傳回。

    使用sax方式不會造成記憶體溢出,也可以實作查詢的操作。

    但是使用sax方式,不能實作增删改的操作。

解析器

想要解析xml,首先需要解析器。

不同的公司群組織提供了針對dom和sax方式的解析器,通過api方式提供。

  • SUN公司提供了針對dom和sax的解析器,叫 jaxp。
  • dom4j組織,針對dom和sax解析,提供了 dom4j。這個解析器實際開發中應用的最廣泛。
  • jdom組織,針對dom和sax解析,提供了 jdom。

六、JAXP

JAXP:Java API for XMLProcessing,意為XML處理的Java API。它是JavaSE的一部分。

jaxp解析器在jdk的javax.xml.parsers包裡面。

裡面有四個類,分别是針對dom和sax解析使用的類。

針對DOM

DocumentBuilder:解析器類

這個類是一個抽象類,不能new。

但是可以使用

DocumentBuilderFactory.newDocumentBuilder()

方法擷取執行個體。

可以使用方法來解析xml:

parse(xml的路徑)

,傳回的是一個Document接口。

方法:

Document是一個接口,父接口是Node,如果在Document中找不到相應的方法,就去Node裡面去找。

Document裡面的

getElementsByTagName(String tagname)

方法 可以得到标簽,傳回的是一個集合NodeList。

createElement(String tagName)

:建立标簽

createTextNode(String data)

:建立文本

appendChild(Node newChild)

:将節點 newChild 添加到此節點的子節點清單的末尾。

removeChild(Node oldChild)

:從子節點清單中移除 oldChild 所訓示的子節點,并将其傳回。

replaceChild(Node newChild, Node oldChild)

:将子節點清單中的子節點 oldChild 替換為 newChild,并傳回 oldChild 節點。

getParentNode()

:得到此節點的父節點。

getFirstChild()

:此節點的第一個子節點。

getLastChild()

:此節點的最後一個節點。

這些方法不存在相容性問題,可以放心使用。

前面得到的NodeList對象,可以使用

getLength()

擷取清單節點數,使用

item(int index)

傳回集合中的第 index 個項。

例如:

NodeList list

for(int i = 0; i < list.getLength(); i++){
	list.item(i);
}
           

DocumentBuilderFactory:解析器工廠

這個類也是一個抽象類,不能new,可以通過

newInstance()

方法擷取 DocumentBuilderFactory 的執行個體。

針對SAX

SAXParser:解析器類

SAXParserFactory:解析器工廠

使用JAXP實作查詢操作

  • 建立一個xml檔案:
<?xml version="1.0" encoding="UTF-8"?>
<person>
	<p1>
		<name>weijia</name>
		<age>21</age>
	</p1>
	<p2>
		<name>lingjie</name>
		<name>22</name>
	</p2>
</person>
           
  • 查詢xml中所有的name元素的值。
    1. 建立解析器工廠類
    2. 根據解析器工廠類建立解析器
    3. 解析xml傳回Document
    4. 得到所有的name元素
    5. 傳回集合,周遊集合,得到每一個name元素
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document document = db.parse("src/person.xml");
    NodeList nl = document.getElementsByTagName("name");
    for (int i = 0; i < nl.getLength(); i++) {
    	Node name1 = nl.item(i);
    	System.out.println(name1.getTextContent());
    }
               

使用JAXP實作查詢某一個節點

查詢xml中第一個name元素的值

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse("src/person.xml");
NodeList nl = d.getElementsByTagName("name");
Node node = nl.item(0);
System.out.println(node.getTextContent());
           

使用JAXP添加一個節點

我們p1标簽添加一個子标簽

<sex>

,并且标簽内容設定為“女”。

public static void addSex() throws Exception {
	// 建立解析器工廠
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	// 建立解析器
	DocumentBuilder db = dbf.newDocumentBuilder();
	// 解析xml,得到document
	Document d = db.parse("src/person.xml");
	// 得到所有的p1
	NodeList eles = d.getElementsByTagName("p1");
	// 得到第一個p1
	Node ele = eles.item(0);
	// 建立sex标簽
	Element newElement = d.createElement("sex");
	// 建立文本元素,内容為nv
	Text text = d.createTextNode("nv");
	// 把文本元素放入sex标簽
	newElement.appendChild(text);
	// 把sex标簽放入第一個p1标簽
	ele.appendChild(newElement);

	// 回寫xml檔案
	TransformerFactory factory = TransformerFactory.newInstance();
	Transformer transformer = factory.newTransformer();
	transformer.transform(new DOMSource(d), new StreamResult("src/person.xml"));
}
           

使用JAXP修改節點

把p1标簽的sex标簽内容設定為“nan”。

public static void modSex() throws Exception {
	// 建立解析器工廠
	DocumentBuilderFactory dbt = DocumentBuilderFactory.newInstance();
	// 建立解析器
	DocumentBuilder db = dbt.newDocumentBuilder();
	// 解析xml,得到document
	Document document = db.parse("src/person.xml");

	// 得到sex1
	Node sex1 = document.getElementsByTagName("sex").item(0);
	// 設定為nan
	sex1.setTextContent("man");

	// 回寫xml檔案
	TransformerFactory factory = TransformerFactory.newInstance();
	Transformer transformer = factory.newTransformer();
	transformer.transform(new DOMSource(document), new StreamResult("src/person.xml"));
}
           

使用JAXP删除節點

把sex标簽删除掉。

public static void deleteSex() throws Exception {
	// 建立解析器工廠
	DocumentBuilderFactory dbt = DocumentBuilderFactory.newInstance();
	// 建立解析器
	DocumentBuilder db = dbt.newDocumentBuilder();
	// 解析xml,得到document
	Document document = db.parse("src/person.xml");

	// 得到sex标簽
	NodeList sex = document.getElementsByTagName("sex");
	// 判斷是否有這個标簽
	if (sex.getLength() != 0) {
		// 得到第一個sex标簽
		Node sex1 = sex.item(0);
		// 得到sex1的父标簽,也就是p1标簽
		Node p1 = sex1.getParentNode();
		// 删除sex1标簽
		p1.removeChild(sex1);

		// 回寫xml檔案
		TransformerFactory factory = TransformerFactory.newInstance();
		Transformer transformer = factory.newTransformer();
		transformer.transform(new DOMSource(document), new StreamResult(
				"src/person.xml"));
	}
}
           

使用JAXP周遊節點

把xml中所有的元素名稱列印出來。

使用遞歸來實作:

public static void list() throws Exception {
	/*
	 * 1. 建立解析器工廠類 2. 根據解析器工廠類建立工廠類 3. 解析xml,傳回document
	 */
	DocumentBuilderFactory builderFactory = DocumentBuilderFactory
			.newInstance();
	DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
	Document document = documentBuilder.parse("src/person.xml");

	Node firstChild = document.getFirstChild();
	/*
	 * ====使用遞歸來實作==== 得到根節點 得到根節點的子節點 得到子節點的子節點
	 */
	list1(firstChild);
}

private static void list1(Node node) {
	// 判斷一下是不是标簽,如果是空格和換行的話,就跳過
	if (node.getNodeType() == Node.ELEMENT_NODE) {
		System.out.print("<");
		System.out.print(node.getNodeName());
		System.out.println(">");
	}
	NodeList list = node.getChildNodes();
	for (int i = 0; i < list.getLength(); i++) {
		Node node1 = list.item(i);
		list1(node1);
	}
}