XML語言
一、XML是什么?作用是什么?
l XML ( eXtensible Markup Language )語言是一種可擴展的標記語言。其中的可擴展是相對HTML來說的。因為XML標簽沒有被預定義,需要用戶自行定義標簽。
l XML 被設計的宗旨是:是表示數據,而非顯示數據。
作用:
l Java開發中,傳統的配置文件是*.properties屬性文件(key=value),而XML表示的數據更為豐富。
l XML技術除用於描述有關系的數據外,還經常用作軟件配置文件,以描述程序模塊之間的關系。如:
這樣的關系數據該如何處理?
用XML語言處理:
總結:在XML語言中,它允許用戶自定義標簽,一個標簽用於描述一段數據,一個標簽分為開始標簽和結束標簽,在這兩者之間又可以使用其它標簽描述其他數據,以此來實現數據關系的描述。
二、XML的基本語法
1、文檔聲明
文檔的聲明必須出現在第一行,之前連空行和注釋都不能有.
文檔聲明, 最簡單的語法: <?xml version="1.0"?>
encoding="UTF-8", 如果不添加,默認的采用的是UTF-8編碼,保存在磁盤上的編碼要與聲明的編碼一樣!
standalone屬性,用來說明文檔是否獨立,即文檔是否依賴其他文檔。
<?xml version="1.0" encoding="utf-8"?>
2、元素
1) xml 元素是指xml中的標簽。一個標簽分為開始標簽和結束標簽:
a) 包含標簽主體:<mytag>some content</mytag>
b) 不含標簽主體:<mytag/>
2) 一個XML文檔必須有且僅有一個根標簽,其他標簽都是這個根標簽的子標簽或孫標簽。
3) 一個標簽中可以嵌套若干子標簽,但所有標簽必須合理的嵌套,不允許有交叉嵌套。
4) xml 中的空格與換行不會被忽略,會當做原始內容被處理.
5) 一個XML元素可以包含字母、數字以及其它一些可見字符,但必須遵守下面的一些規范:
a) 區分大小寫,例如,<P>和<p>是兩個不同的標記。
b) 不能以數字或"_" (下划線)開頭。
c) 不能以xml(或XML、或Xml 等)開頭。
d) 不能包含空格。
e) 名稱中間不能包含冒號(:) (有特殊用途)。
3、屬性
1) 一個標簽可以有多個屬性,每個屬性都有它自己的名稱和取值,例如: <mytag name="value"/>
2) 屬性值一定要用雙引號(")或單引號(')引起來
3) 定義屬性必須遵循與標簽相同的命名規范
4) 在XML技術中,標簽屬性所代表的信息,也可以被改成用子元素的形式來描述。
4.注釋
注釋的語法: <!--這是注釋哦.-->
注釋不能寫在第一行.
注釋不能夠嵌套.
1 <?xml version="1.0" encoding="utf-8"?> 2 <!-- 注釋 --> 3 <書架> 4 <書 出版社="中國上海"> 5 <名字>誅仙</名字> 6 <作者>蕭鼎</作者> 7 <價格>32.00</價格> 8 <出版日期>2007年</出版日期> 9 </書> 10 <書 出版社="中國北京"> 11 <名字>笑傲江湖</名字> 12 <作者>金庸</作者> 13 <價格>50.00</價格> 14 </書> 15 </書架>
5.CDATA區
用於將一段內容當做普通文本.
語法:<![CDATA[
文本內容
]]>
6.特殊字符
& & ampersand
< < less than
> > great than
" &;quot; quotation
' ' apostrophe
7.處理指令(processing instruction) (PI)
作用:用來指揮軟件如何解析XML文檔
語法: <?xml ?>
如:常用的PI指令:
XML聲明:<?xml version=“1.0” encoding=“GB2312”?>
xml-stylesheet指令:<?xml-stylesheet type = “text/css” herf=”test.css”>
<?xml version="1.0" encoding="UTF-8"?> <!-- 這里用處理命令加入css樣式 --> <?xml-stylesheet type="text/css" href = "PITest.css" ?> <中國> <北京>北京</北京> <上海>上海</上海> <深圳>深圳</深圳> </中國>
PITest.css 文檔如下:
1 @CHARSET "UTF-8"; 2 北京{ 3 font-size: 100px ; 4 color: red; 5 } 6 上海{ 7 font-size: 110px ; 8 color: green ; 9 } 10 深圳{ 11 font-size:100px ; 12 color: yellow ; 13 }
三、XML的約束
1、在XML 技術中可以編寫一個文檔來約束XML 文檔里面的書寫規范,這稱為XML約束。
2、XML 約束技術:
常用的有:XML DTD 和 XML Schema
3、XML 約束的必要性:
a) XML都是用戶自定義的標簽,若出現小小的錯誤,軟件程序將不能正確地獲取文件中的內容而報錯.
總結:
格式良好的XML 文檔,遵循語法規則的XML 文檔。
有效的XML 文檔,遵循約束文檔的 XML 文檔。
約束文檔定義了在XML中允許出現的元素名稱、屬性及元素出現的順序等等。
四、DTD的基本語法
1.DTD 約束的兩種方式:
DTD 約束可以作為一個單獨的文檔編寫,也可以編寫在XML 文檔內。(編寫XML內部的DTD代碼),當作為單獨文件時,要用utf-8格式存儲。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 在XML 寫入DTD 約束 --> 3 <!DOCTYPE 世界[ 4 <!ELEMENT 世界 (國家+) > 5 <!ELEMENT 國家 (名字)> 6 <!ELEMENT 名字 (#PCDATA) > 7 <!ATTLIST 名字 8 所屬洲 CDATA #IMPLIED 9 > 10 <!ATTLIST 國家 所屬洲 (亞洲|歐洲|南美洲|南極洲|澳洲|非洲|北美洲) "亞洲"> 11 ]> 12 13 <世界> 14 <國家 所屬洲 = "亞洲"> 15 <名字>中國</名字> 16 </國家> 17 <國家> 18 <名字 所屬洲 = "美洲">美國</名字> 19 </國家> 20 </世界>
XML 引用 DTD 約束文件時:
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE 書架 SYSTEM "book.dtd" > 3 <書架> 4 <書> 5 <名字>誅仙</名字> 6 <作者>蕭鼎</作者> 7 <價格>32.00</價格> 8 </書> 9 <書 > 10 <名字>笑傲江湖</名字> 11 <作者>金庸</作者> 12 <價格>50.00</價格> 13 </書> 14 </書架>
a)本地的 : <!DOCTYPE 根元素 SYSTEM “DTD文檔路徑”>
b)互聯網上的: <!DOCTYPE 根元素 PUBLIC “DTD文檔路徑” "dtd的URL地址">
如:<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
注意book.dtd文檔如下:
<!ELEMENT 書架 (書+)> <!ELEMENT 書 (名字,作者,價格)> <!ELEMENT 名字 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 價格 (#PCDATA)>
DTD 編寫細節:
1、 元素定義
在DTD 文件中用ELEMENT 聲明一個 XML元素,語法:
<!ELEMENT 元素名稱 元素類型>
元素類型可以是元素內容。
元素內容,則需要用() 括起來,
<!ELEMENT 世界 (國家+) >
<!ELEMENT 國家 (名字,所屬洲)>
<!ELEMENT 名字 (#PCDATA)>
元素類型的組成遵循正則表達式的格式:
1、用逗號分隔,表示內容的出現順序必須與聲明時一致。
<!ELEMENT MYFILE (TITLE,AUTHOR,EMAIL)>
2、用|分隔,表示任選其一,即多個只能出現一個
<!ELEMENT MYFILE (TITLE|AUTHOR|EMAIL)>
3、在元素內容中也可以使用+、*、?等符號表示元素出現的次數:
+: 一次或多次 (書+) regex
?: 0次或一次 (書?)
*: 0次或多次 (書*)
4、也可使用圓括號( )批量設置,例
<!ELEMENT FILE ((TITLE*, AUTHOR?, EMAIL)* | COMMENT)>
元素類型,則直接書寫,DTD規范定義了如下幾種類型:
EMPTY:用於定義空元素,例如<br/> <hr/>
ANY:表示元素內容為任意類型。
2、 屬性定義
xml文檔中的標簽屬性需通過ATTLIST為其設置屬性
語法格式:
<!ATTLIST 元素名
屬性名1 屬性值類型 設置說明
屬性名2 屬性值類型 設置說明
……
>
設置說明:
#REQUIRED:必須設置該屬性
#IMPLIED:可以設置也可以不設置
#FIXED:說明該屬性的取值固定為一個值,在 XML 文件中不能為該屬性設置其它值。但需要為該屬性提供這個值
直接使用默認值:在 XML 中可以設置該值也可以不設置該屬性值。若沒設置則使用默認值。但需要為該屬性提供這個值
常用屬性值類型
l CDATA:表示屬性值為普通文本字符串。
l ENUMERATED
l ID indentity
l ENTITY(實體)
1 <!ELEMENT 國家 (名字)> 2 <!ATTLIST 國家 3 所屬洲 CDATA #REQUEIRED >
3、 實體定義
實體用於為一段內容創建一個別名,以后在XML 文檔中就可以使用別名引用這段內容。
實體可分為兩種類型:引用實體和參數實體。
引用實體主要在XML 文檔中被使用:
語法格式:<!ENTITY 實體名稱 “實體內容” >: 直接轉變成實體內容。
引用方式:&實體名稱;
參數實體則是在DTD 文檔中自身使用:
語法格式:<!ELEMENT % 實體名稱 “實體內容” >
引用方式 :%實體名稱
<!ENTITY % TAG_NAMES "姓名 | EMAIL | 電話 | 地址"> <!ELEMENT 個人信息 (% TAG_NAMES; | 生日)> <!ELEMENT 客戶信息 (% TAG_NAMES; | 公司名)>
五、JUnit 測試類
JUnit 作為測試工具,可以通過注釋的方法來代替寫main方法,同時來測試相應的方法:再用JUbit 之前 先導入junit.jar 包。
@Test :用來注釋需要被測試的方法。
@Before : 用來注釋在運行@Test方法之前需要被運行的方法。 注意:如果有多個方法被@Before 注釋,則從最后一個開始運行。
@After : 則注釋在運行@Test方法之后需要被運行的方法。 注意:如果有多個方法被@After 注釋,則從第一個開始運行。
@AfterClass :注釋的方法在類釋放時運行。
@BeforeClass : 注釋的方法在類加載時運行。
1 import org.junit.After; 2 import org.junit.AfterClass; 3 import org.junit.Before; 4 import org.junit.BeforeClass; 5 import org.junit.Test; 6 7 8 public class JUnitDemo { 9 @Test 10 public void test_1() { 11 System.out.println("Hello JUnit!"); 12 } 13 @Before 14 public void before(){ 15 System.out.println("Before"); 16 } 17 @AfterClass 18 public static void afterClass(){ 19 System.out.println("AfterClass"); 20 } 21 @After 22 public void after(){ 23 System.out.println("After"); 24 } 25 @BeforeClass 26 public static void beforeClass(){ 27 System.out.println("BeforeClass"); 28 } 29 }
六、JAXP進行DOM解析
XML 的兩種解析方式:DOM 解析和SAX 解析。
DOM (Document Object Model ,文檔對象模式 ) 解析,原理DOM解析器在解析XML文檔時,會把文檔中的所有元素(document\element\attribute\character),按照其出現的層次關系,解析成一個個Node對象(節點)
在dom中,節點之間關系如下:
1、 位於一個節點之上的節點是該節點的父節點(parent)
2、 一個節點之下的節點是該節點的子節點(children)
3、 同一層次,具有相同父節點的節點是兄弟節點(sibling)
4、 一個節點的下一個層次的節點集合是節點后代(descendant)
5、祖父節點及所有位於節點上面的,都是節點的祖先(ancestor)
Node對象提供了一系列常量來代表結點的類型,當開發人員獲得某個Node類型后, 就可以把Node節點轉換成相應的節點對象(Node的子類對象),以便於調用其特有的方法。
Node對象提供了相應的方法去獲得它的父結點或子結點。編程人員通過這些方法就可以讀取整個XML文檔的內容、或添加、修改、刪除XML文檔的內容了。
練習:
1.讀取節點的文本內容
2.讀取屬性值
3.添加節點
4.刪除節點
5.更新節點
6.打印所有元素節點的名稱.
1 package cn.itheima.xml.day01; 2 3 import javax.xml.parsers.DocumentBuilderFactory; 4 import javax.xml.transform.TransformerFactory; 5 import javax.xml.transform.dom.DOMSource; 6 import javax.xml.transform.stream.StreamResult; 7 8 import org.junit.After; 9 import org.junit.Before; 10 import org.junit.Test; 11 import org.w3c.dom.Document; 12 import org.w3c.dom.Element; 13 import org.w3c.dom.NamedNodeMap; 14 import org.w3c.dom.Node; 15 import org.w3c.dom.NodeList; 16 17 public class DOMTest { 18 /* 19 1.讀取節點的文本內容 20 2.讀取屬性值 21 3.添加節點 22 4.刪除節點 23 5.更新節點 24 6.打印所有元素節點的名稱. 25 */ 26 /* 27 * DOM解析器在解析XML文檔時,會把文檔中的所有元素(document\element\attribute\character),按照其出現的層次關系, 28 * 解析成一個個Node對象(節點)。 29 * 30 * Node對象提供了一系列常量來代表結點的類型,當開發人員獲得某個Node類型后, 31 * 就可以把Node節點轉換成相應的節點對象(Node的子類對象),以便於調用其特有的方法。 32 */ 33 34 //1、讀取節點的文本內容 35 private Document doc = null ; 36 @Test 37 public void readContent() { 38 //獲得標簽名為"名字" 的 NodeList ; 39 NodeList lists = doc.getElementsByTagName("名字") ; 40 //獲得節點的文本類容 41 for (int i = 0; i < lists.getLength(); i++) { 42 System.out.println(lists.item(i).getTextContent()); 43 } 44 } 45 //2、讀取屬性值 46 @Test 47 public void getAttr() { 48 //獲取元素“書”的NodeList 49 NodeList lists = doc.getElementsByTagName("書") ; 50 //通過遍歷lists ,獲取每個節點中的屬性值 51 for (int i = 0; i < lists.getLength(); i++) { 52 NamedNodeMap attributes = lists.item(i).getAttributes(); 53 for (int j = 0; j < attributes.getLength(); j++) { 54 System.out.println(attributes.item(j).getTextContent()); 55 } 56 } 57 } 58 //3.添加節點 59 //在“書”標簽下添加一個<出版日期>的元素 60 @Test 61 public void addNode() throws Exception{ 62 //創建一個<出版日期> 添加到內存中 63 Element element = doc.createElement("出版日期") ; 64 //設置該標簽的的文本值 65 element.setTextContent("2007年"); 66 //通過Node 類中的 appendChild 方法將<出版日期>添加到節點的子節點列表的末尾 67 NodeList lists = doc.getElementsByTagName("書"); 68 69 //問題:為什么只在第二個"書" 標簽添加了該子節點。 70 //而且如果存在<出版日期> 子節點,為什么添加之后有兩個該節點。 71 /* 72 Node appendChild(Node newChild) throws DOMException 73 將節點 newChild 添加到此節點的子節點列表的末尾。如果 newChild 已經存在於樹中,則首先移除它。 74 */ 75 for (int i = 0 ; i < lists.getLength() ; i ++) { 76 System.out.println(lists.item(i).getNodeName()); 77 lists.item(i).appendChild(element) ; 78 } 79 updateXML() ; 80 } 81 82 //如何將修改后的DOC寫到XML中? 83 public void updateXML() throws Exception { 84 /* 85 * javax.xml.transform包中的Transformer類用於把代表XML文件的Document對象轉換為某種格式后進行輸出 86 * Transformer類通過transform方法完成轉換操作,該方法接收一個源和一個目的地。我們可以通過: 87 * > javax.xml.transform.dom.DOMSource 類來關聯要轉換的document對象, 88 * > javax.xml.transform.stream.StreamResult 對象來表示數據的目的地。 89 */ 90 91 //創建TransformFactory 對象: 92 TransformerFactory.newInstance() 93 .newTransformer() 94 .transform(new DOMSource(doc), new StreamResult("src/cn/itheima/xml/day01/book.xml")); 95 } 96 97 //4.刪除節點 98 //刪除“出版日期”節點。 99 @Test 100 public void removeNode() throws Exception { 101 //獲取 "出版日期" 的節點 102 Node node = doc.getElementsByTagName("出版日期").item(0) ; 103 104 //獲取 node 節點的父節點。 105 //通過父節點刪除"出版日期"節點 106 node.getParentNode().removeChild(node) ; 107 updateXML() ; 108 } 109 110 //5.更新節點 111 //將 "笑傲江湖" 的“價格”修改為 50.00 112 /* 113 * 1、獲取“名字”的NodeList , 然后遍歷 值為“笑傲江湖” 的節點。 114 * 2、獲取“笑傲江湖”節點的兄弟節點。 115 */ 116 @Test 117 public void updateNode() throws Exception{ 118 //獲取“笑傲江湖”的節點 119 NodeList nodeList = doc.getElementsByTagName("名字"); 120 for (int i = 0; i < nodeList.getLength(); i++) { 121 // System.out.println(nodeList.item(i).getTextContent()); 122 if ( nodeList.item(i).getTextContent().equals("笑傲江湖")) { 123 NodeList childNodes = nodeList.item(i).getParentNode().getChildNodes(); 124 for (int j = 0; j < childNodes.getLength(); j++) { 125 if (childNodes.item(j).getNodeName().equals("價格")) { 126 childNodes.item(j).setTextContent("50.00") ; 127 break ; 128 } 129 } 130 } 131 } 132 //通過獲取 其父節點然后通過父節點獲取到“價格”節點,修改其節點的值 133 /*NodeList lists = node.getParentNode().getChildNodes() ; 134 for(int i = 0 ; i < lists.getLength() ; i++) { 135 if( lists.item(i).getNodeName().equals("價格") ) { 136 lists.item(i).setTextContent("50.00"); 137 break ; 138 } 139 }*/ 140 updateXML() ; 141 } 142 //6.打印所有元素節點的名稱. 143 @Test 144 public void printNode(){ 145 treeWeek(doc) ; 146 } 147 public void treeWeek(Node node) { 148 if(Node.ELEMENT_NODE == node.getNodeType()){ 149 System.out.println(node.getNodeName()); 150 } 151 NodeList nl = node.getChildNodes(); 152 for (int i = 0; i < nl.getLength(); i++) { 153 Node item = nl.item(i); 154 treeWeek(item); 155 } 156 } 157 @Before 158 public void getDOM() throws Exception{ 159 /* 160 * 1、獲得DocumentBuilderFactory 對象。 161 * 2、通過DocumentBuilderFactory 對象創建 DocumentBuilder 對象(DOM 解析器對象)。 162 * 3、通過DocumentBuilder 對象解析XML文件,進而可以利用DOM特性對整個XML文檔進行操作了。 163 */ 164 doc = DocumentBuilderFactory.newInstance() 165 .newDocumentBuilder() 166 .parse("src/cn/itheima/xml/day01/book.xml") ; 167 } 168 @After 169 public void setDOM() { 170 doc = null ; 171 } 172 }
總結:
DOM 解析 的優點是增刪改方便,
缺點,如果要解析的XML 文檔過大,就會導致內存溢出(Out Of Memory , OOM),因為DOM解析需要將XML 文檔內容全部加載到內存中再解析。
七、JAXP進行SAX解析
l SAX (Simple API for XML ) 解析:SAX解析允許在讀取文檔的時候,即對文檔進行處理,而不必等到整個文檔裝載完才對文檔進行操作。
l SAX采用事件處理的方式解析XML文件,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器:
l 解析器可以使用JAXP的API創建,創建出SAX解析器后,就可以指定解析器去解析某個XML文檔。
l 解析器采用SAX方式在解析某個XML文檔時,它只要解析到XML文檔的一個組成部分,都會去調用事件處理器的一個方法,解析器在調用事件處理器的方法時,會把當前解析到的xml文件內容作為方法的參數傳遞給事件處理器。
l 事件處理器由程序員編寫,程序員通過事件處理器中方法的參數,就可以很輕松地得到sax解析器解析到的數據,從而可以決定如何對數據進行處理。
SAX解析步驟:
1、使用SAXParserFactory創建SAX解析工廠
2、通過SAX解析工廠得到解析器對象
3、通過解析器對象得到一個XML的讀取器
4、設置讀取器的事件處理器
5、解析xml文件
package cn.itheima.xml.day01; import java.io.IOException; import javax.xml.parsers.SAXParserFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /* * SAX解析允許在讀取文檔的時候,即對文檔進行處理,而不必等到整個文檔裝載完才對文檔進行操作。 * * SAX采用事件處理的方式解析XML文件,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器: * 解析器可以使用JAXP的API創建,創建出SAX解析器后,就可以指定解析器去解析某個XML文檔。 * * 解析器采用SAX方式在解析某個XML文檔時,它只要解析到XML文檔的一個組成部分(startDocument,startElement,character,endElement,endDocument), * 都會去調用事件處理器的一個方法,解析器在調用事件處理器的方法時, * 會把當前解析到的xml文件內容作為方法的參數傳遞給事件處理器。 * * 事件處理器由程序員編寫,程序員通過事件處理器中方法的參數, * 就可以很輕松地得到sax解析器解析到的數據,從而可以決定如何對數據進行處理。 * javax.xml.parsers.SAXParserFactory * */ /* SAX 解析練習. 1.讀取節點的文本內容 2.讀取屬性值 3.添加節點 4.刪除節點 5.更新節點 6.打印所有元素節點的名稱. 思路: 1、使用SAXParserFactory創建SAX解析工廠 2、通過SAX解析工廠得到解析器對象 3、通過解析器對象得到一個XML的讀取器 4、設置讀取器的事件處理器 5、解析xml文件 */ public class SAXTest { private XMLReader reader = null ; //打印出解析出的XML所有內容 @Test public void printTest() throws IOException, SAXException { //設置讀取器的事件處理器: reader.setContentHandler(new MyHandler()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } //1.讀取節點的文本內容 /* * 讀取節點名字為“名字”的文本內容 */ @Test public void getTextContent() throws IOException, SAXException { reader.setContentHandler(new getTestContent()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } //2.讀取屬性值 /* * 讀取節點名字為"書" 的屬性值 */ @Test public void getAttributeName() throws IOException, SAXException { reader.setContentHandler(new getAttribute()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } // 在測試之前獲得一個XML讀取器: @Before public void getReader() throws Exception { /* 1、使用SAXParserFactory創建SAX解析工廠 2、通過SAX解析工廠得到解析器對象 3、通過解析器對象得到一個XML的讀取器 */ reader = SAXParserFactory.newInstance() .newSAXParser() .getXMLReader() ; } //測試完重置reader = null @After public void setReader() { reader = null ; } } class getAttribute extends DefaultHandler { @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("書".equals(qName)) { for (int i = 0; i < attributes.getLength(); i++) { System.out.println(attributes.getValue(i)); } } } } class getTestContent extends DefaultHandler{ private boolean flag = false ; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("名字".equals(qName)) { flag = true ; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (flag){ System.out.println("文本類容:"+new String(ch,start,length)); flag = false ; } } } //編寫事件處理器: /* 繼承org.xml.sax.helpers.DefaultHandler * DefaultHandler 類: * SAX2 事件處理程序的默認基類。應用程序編寫者可以在他們僅需實現部分接口時擴展此類; * 類似於ContentHandler 接口的適配器(adapter) ; */ class MyHandler extends DefaultHandler { //在此類中重寫我們需要的幾個方法: @Override public void startDocument() throws SAXException { System.out.println("XML文件開始解析:"); } @Override public void endDocument() throws SAXException { System.out.println("XML文件解析結束:"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("元素標簽開始:"+ qName); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("元素標簽結束:" + qName); } @Override public void characters(char[] ch, int start, int length) throws SAXException { System.out.println("文本類容:"+new String(ch,start,length)); } }
總結:優點查找非常快,但是沒DOM 解析 方法那樣直觀明白。
八、Dom4J簡介、進行解析
Dom4J 則是一層一層的解析XML文件,而且直觀。
1 package cn.itheima.xml.day01; 2 3 import java.io.FileNotFoundException; 4 import java.io.FileOutputStream; 5 import java.io.UnsupportedEncodingException; 6 import java.util.Iterator; 7 import java.util.List; 8 9 import org.dom4j.Attribute; 10 import org.dom4j.Document; 11 import org.dom4j.DocumentException; 12 import org.dom4j.Element; 13 import org.dom4j.io.OutputFormat; 14 import org.dom4j.io.SAXReader; 15 import org.dom4j.io.XMLWriter; 16 import org.junit.After; 17 import org.junit.Before; 18 import org.junit.Test; 19 20 /* 21 * Dom4j是一個非常優秀的Java XML API,具有性能優異、功能強大和極易使用的特點。 22 */ 23 public class DOM4JTest { 24 /* 25 1.讀取節點的文本內容 26 2.讀取屬性值 27 3.添加節點 28 4.刪除節點 29 5.更新節點 30 6.打印所有元素節點的名稱. 31 */ 32 private Document doc = null ; 33 //1.讀取節點的文本內容 34 /* 35 * 讀取"書"節點下,“名字”節點的文本內容 36 * DOM4J 思路: 37 * 1.獲取文檔的根節點. 38 Element root = document.getRootElement(); 39 40 2.取得某個節點的子節點. 41 Element element=node.element(“書名"); 42 43 3.取得節點的文字 44 String text=node.getText(); 45 46 */ 47 @Test 48 public void getNodeText() { 49 //獲得跟節點 50 Element root = doc.getRootElement() ; 51 //通過跟節點獲取到子節點 52 List<Element> lists = root.elements() ; 53 for (Element ele : lists) { 54 List<Element> elements = ele.elements() ; 55 for (Element element : elements) { 56 if (element.getName().equals("名字")) 57 System.out.println(element.getText()); 58 } 59 } 60 } 61 //2.讀取屬性值 62 @Test 63 public void getAttribute () { 64 Element root = doc.getRootElement() ; 65 //通過跟節點獲取到子節點 66 List<Element> lists = root.elements() ; 67 for(Element ele : lists) { 68 List<Attribute> attributes = ele.attributes(); 69 for (Attribute attribute : attributes) { 70 System.out.println(attribute.getText()); 71 } 72 } 73 } 74 //3.添加節點 75 /* 76 * 在“書”節點下添加“出版日期”節點。 77 */ 78 @Test 79 public void addNode() throws Exception { 80 Element root = doc.getRootElement() ; 81 for (Iterator<Element> it = root.elementIterator(); it.hasNext() ;) { 82 it.next().addElement("出版日期") 83 .setText("2007年") ; 84 } 85 //寫到XML文件中去。 86 writeToXML() ; 87 } 88 //4.刪除節點 89 /* 90 * 刪除“書”節點下,“名字”為“笑傲江湖”的“出版日期”的節點。 91 * 思路: 92 * 1、獲取根元素。 93 * 2、通過根元素獲取其子元素,。 94 * 3、遍歷其子元素,獲取子元素的"名字"元素。 95 * 4、如果"名字"元素的值符合條件。 96 * 5、則刪除其兄弟元素"出版日期" 。 97 */ 98 @Test 99 public void removeNode() throws Exception{ 100 Element root = doc.getRootElement() ; 101 // 102 for (Iterator<Element> it = root.elementIterator(); it.hasNext() ;) { 103 Element element = it.next() ; 104 if (element.element("名字").getText().equals("笑傲江湖")) 105 element.remove(element.element("出版日期")) ; 106 } 107 writeToXML() ; 108 } 109 public void writeToXML() throws Exception{ 110 111 //在寫入XML文件時,要設置寫入的編碼格式:utf-8 112 OutputFormat format = OutputFormat.createPrettyPrint(); 113 format.setEncoding("utf-8") ; 114 //最好不要用 FileWriter 寫入,因為,FileWriter 寫入時默認為系統設定的編碼! 115 XMLWriter writer = new XMLWriter( 116 new FileOutputStream("src/cn/itheima/xml/day01/book.xml"),format ); 117 writer.write( doc ); 118 writer.close(); 119 } 120 @Before 121 public void getDoc() throws Exception { 122 doc = new SAXReader().read( "src/cn/itheima/xml/day01/book.xml"); 123 } 124 @After 125 public void setDoc() { 126 doc = null ; 127 } 128 }
九、XML約束之schema
l XML Schema 文件自身就是一個XML文件,但它的擴展名通常為.xsd。
l 一個XML Schema文檔通常稱之為模式文檔(約束文檔),遵循這個文檔書寫的xml文件稱之為實例文檔。
l 和XML文件一樣,一個XML Schema文檔也必須有一個根結點,但這個根結點的名稱為Schema。
l 編寫了一個XML Schema約束文檔后,通常需要把這個文件中聲明的元素綁定到一個URI地址上,在XML Schema技術中有一個專業術語來描述這個過程,即把XML Schema文檔聲明的元素綁定到一個名稱空間上,以后XML文件就可以通過這個URI(即名稱空間)來告訴解析引擎,xml文檔中編寫的元素來自哪里,被誰約束。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 3 在XML Schema中,每個約束模式文檔都可以被賦以一個唯一的名稱空間, 4 名稱空間用一個唯一的URI(Uniform Resource Identifier,統一資源標識符)表示 5 6 名稱空間: 7 targetNamespace="http://jbelial.cnblogs.com" 8 URL:http://jbelial.cnblogs.com 根本沒有指向任何文件,只是一個分配的名字。 9 10 elementFormDefault="qualified" 11 12 elementFormDefault元素用於指定, 13 該schema文檔中聲明的根元素及其所有子元素都屬於targetNamespace所指定的名稱空間。 14 15 --> 16 <schema xmlns="http://www.w3.org/2001/XMLSchema" 17 targetNamespace="http://jbelial.cnblogs.com" 18 xmlns:tns="http://www.example.org/NewXMLSchema" 19 elementFormDefault="qualified"> 20 <!-- 規定根元素 --> 21 <element name='書架'> 22 <!-- 根元素下存放復雜數據類型 --> 23 <complexType> 24 <!-- 根元素下的元素的排列方式,和數目為"未綁定" --> 25 <sequence maxOccurs='unbounded'> 26 <element name='書'> 27 <complexType> 28 <sequence> 29 <!-- 約束:元素的名字,和接收類型: --> 30 <element name="名字" type="string" /> 31 <element name="作者" type="string" /> 32 <element name="價格" type="string" /> 33 </sequence> 34 </complexType> 35 </element> 36 </sequence> 37 </complexType> 38 </element> 39 </schema>
在用Schema 約束XML 文檔時,要注意一下問題:
a)你要創建的xml文檔中的根標簽是什么?
第一個出現的 <xs:element name='書架' > 就是根標簽
b) 思考: 你使用這個根標簽它來自哪個名稱空間.
在schema約束文檔中的 targetNamespace="http://jbelial.cnblogs.com" 就是用來說明所有跟標簽綁定在哪個目標名稱空間上.
c) 思考: 你要引用 schema文檔它與目前名稱空間的對應關系?
需要在xml文檔中添加 xsi:schemaLocation="{namespace} {location}"
{namespace} 就是 : http://jbelial.cnblogs.com
{location} : 引用的schema文檔在哪里
d) 固定的寫法:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<?xml version="1.0" encoding="UTF-8"?> <p:書架 xmlns:p = "http://jbelial.cnblogs.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jbelial.cnblogs.com NewXMLSchema.xsd"> <p:書> <p:名字>射雕英雄傳</p:名字> <p:作者>金庸</p:作者> <p:價格>100.00</p:價格> </p:書> </p:書架>
總結:
XML Schema VS DTD