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來表示這些有關系的資料。
例如:
-
經常用在配置檔案
例如在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中标簽的命名規則
- xml代碼區分大小寫,
<p>
這兩個标簽是不同的。<P>
- 标簽名稱不能以
或者數字
開頭。下劃線
<2a>
都不可允許。<_aa>
- 不能以
、xml
、XML
等開頭。Xml
- 标簽裡面不能包含空格和冒号。
a b
都是不允許的。b:c
- 标簽可以使用中文,但是不建議這樣做。
- xml代碼區分大小寫,
定義屬性
html是标記型文檔,可以有屬性。
xml也是标記型文檔,也可以有屬性。
屬性定義的要求:
- 一個标簽上可以有多個屬性
<person id1="aaa" id2="bbb"></person>
- 屬性名稱不能相同。
- 屬性名稱與屬性值之間用
連接配接,屬性值要用引号括起來,可以是單引号,可以是雙引号。=
- 屬性的命名規範和元素的命名規範是一緻的。
注釋
寫法:
<!-- xml的注釋 -->
注意:注釋是不能有嵌套的。
注釋也不能放到第一行!
特殊字元
<a>a<b</a>
如果想在xml中顯示 a<b ,是不能正常顯示的,因為把<當成标簽。
如果想要顯示,就需要使用轉義字元。
常用的轉義字元:
<
< 小于
>
> 大于
&
& 和号
'
’ 單引号
"
" 引号
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的限制
- 為什麼需要限制?
比如我們現在定義一個person的xml檔案,隻想要在裡面儲存人的一些資訊,比如name、age等,但是如果在xml檔案中寫入了一個<?xml version="1.0" encoding="utf-8"?> <person> <name>zhangsan</name> <age>20</age> </person>
的标簽,發現它是可以正常顯示的,因為符合文法規範。但是貓肯定不是人的資訊,是以我們這個時候就需要一種技術來規定xml中隻能出現的元素,這就叫xml的限制。<貓>
- xml中限制的技術:dtd限制和schema限制
dtd限制
我們建立一個檔案,字尾名是
.dtd
。
步驟:
- 看xml中有多少元素,有幾個元素,在dtd檔案中寫幾個
<!ELEMENT>
-
判斷這個元素是簡單元素還是複雜元素。
複雜元素: 有子元素的元素
格式:
例如上面的person:<!ELEMENT 元素名稱 (子元素)>
<!ELEMENT person (name,age)>
簡單元素: 沒有子元素
格式:
例如上面的age、name:<!ELEMENT 元素名稱 (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
- 需要在xml檔案中引入dtd檔案。
<!DOCTYPE 根元素名稱 SYSTEM "dtd檔案的路徑">
如果使用浏覽器打開 xml檔案的話,浏覽器隻負責校驗 xml的文法,不負責校驗 xml的限制。
如果想要校驗 xml的限制,需要使用工具,工具有很多種,例如 MyEclipse。
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檔案)
在structs2 使用配置檔案 使用 外部的dtd檔案,需要用到這個知識。<!DOCTYPE 根元素 PUBLIC "DTD名稱" "dtd文檔的URL">
dtd定義元素
文法:
<!ELEMENT 元素名稱 限制>
- 簡單元素:
<!ELEMENT name (#PCDATA)>
:限制name是字元串類型(#PCDATA)
:元素為空,不能有内容。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:屬性必須存在
#IMPLIED:屬性可有可無#REQUIRED
#FIXED:表示一個固定值#IMPLIED
直接值:表示屬性預設值,當在标簽上不寫這個屬性的時候,就使用直接值,如果寫了屬性,就使用寫的值。#FIXED "AAA"
dtd定義引用實體
概念:在dtd中定義,在xml中使用。
文法:
<!ENTITY 實體名稱 "實體内容">
引用方式:
&實體名稱;
例如:
dtd中定義:
<!ENTITY copyright "veeja版權所有">
xml中引用:
©right;
定義的實體需要使用在内部的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元素的值。
- 建立解析器工廠類
- 根據解析器工廠類建立解析器
- 解析xml傳回Document
- 得到所有的name元素
- 傳回集合,周遊集合,得到每一個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);
}
}