一、XML概述
英文全稱為Extensible Markup Language,翻譯過來為可擴展標記語言。XML技術是W3C組織發布的,目前遵循的是W3C組織於2000發布的XML1.0規范。XML就是為了解決這樣的需求而產生數據存儲格式。
在XML語言中,它允許用戶自定義標簽。每一個標簽用於描述一段數據;一個標簽可以分為開始標簽和結束標簽,在開始標簽和結束標簽之間又可以嵌套其它標簽,利用標簽間的嵌套關系來保存數據之間的上下級關系;由於xml實質上是一段字符串,計算機可以十分方便的對他進行操作,開發人員也可以方便的閱讀,因此可以說這是一種對人、對計算機都友好的數據存儲格式,所以XML迅速普及,成為了一種非常常見的數據存儲格式,在許多應用場景中得到應用。
XML本質上是一段字符串,具有跨平台性的特性,因此XML常被用來在不同系統之間進行數據交換。XML可以在保存數據的同時保存數據之間的關系。利用這一特點,它還經常用作應用程序配置文件來使用。
XML校驗:瀏覽器除了內置HTML解析其外還內置了XML解析器,因此我們可以使用瀏覽器對XML進行校驗。
二、XML語法
一個XML文件分為如下幾部分內容:1.文檔聲明 2.元素 3.屬性 4.注釋 5.CDATA區 6.特殊字符 7.處理指令(processing instruction)
文檔聲明
XML的文檔聲明是用來聲明文檔基本屬性的,XML解析器將根據文檔聲明決定如何正確解析一個XML。通常來說一個XML必須包含且只包含一個文檔聲明;文檔聲明必須處在XML的第一行,前面不能有其他內容;如果一個XML不包含文檔聲明則稱這樣的XML為格式不良好的XML;在許多時候即使不包含文檔聲明,XML也可以被正常使用,但是這是不符合標准的,存在風險,因此強烈推薦大家在書寫XML時寫上文檔聲明。
最簡單寫法: <?xml version="1.0" ?> version 代表當前xml所遵循的xml標准;在第二個問號之前應該有一個空格。注意:問號、引號、空格都必須為英文半角
用encoding屬性說明文檔的字符編碼:<?xml version="1.0" encoding="GB2312" ?> encoding告知解析器使用何種編碼解析當前xml;encoding默認值為ISO8859-1
用standalone屬性說明文檔是否獨立:<?xml version="1.0" encoding="GB2312" standalone="yes" ?> standalone表示當前xml文檔是否是一個獨立文檔,當為yes時表示是一個獨立文檔,當為no時表示當前文檔需要其他文檔支持。
XML元素
一個XML標簽就是一個XML元素。 一個XML標簽分為開始標簽和結束標簽,在開始標簽和結束標簽之間的文本被稱為標簽體。 包含標簽體:<a>www.itcast.cn</a>; 如果一個不包含標簽體也不包含其他元素,那么可以將開始標簽和結束標簽合並,這樣的標簽稱為自閉標簽 不含標簽體及其他元素:<a></a>可以簡寫為自閉標簽:<a/>
一個標簽中也可以嵌套若干子標簽。但所有標簽必須合理的嵌套,絕對不允許交叉嵌套 ,
例如: <a>welcome to www.it315.org<b/></a> <a>welcome to www.it315<b/>.org</a>
格式良好的XML文檔必須有且僅有一個根標簽,其它標簽都是這個根標簽的子孫標簽。
對於XML標簽中出現的所有空格和換行,XML解析程序都會當作標簽內容進行處理。例如:下面兩段內容的意義是不一樣的。
<網址>www.itcast.cn</網址>
<網址>
www.itcast.cn
</網址>
由於在XML中,空格和換行都作為原始內容被處理,所以,在編寫XML文件時,使用換行和縮進等方式來讓原文件中的內容清晰可讀的“良好”書寫習慣可能要被迫改變。
一個XML元素可以包含字母、數字以及其它一些可見字符,但必須遵守下面的一些規范:區分大小寫,例如,<P>和<p>是兩個不同的標記;不能以數字或標點符號或"_"開頭;不能以xml(或XML、或Xml 等)開頭;不能包含空格;名稱中間不能包含冒號(:)。
XML屬性
一個標簽可以有多個屬性,每個屬性都有它自己的名稱和取值,例如:<china capital="beijing"/>
屬性值一定要用雙引號(")或單引號(')引起來,<china capital='beijing'/>
定義屬性名必須遵循與元素相同的命名規范
XML注釋
Xml文件中的注釋采用:“<!--注釋-->” 格式。
注釋不能出現在文檔聲明之前(因為XML要求文檔聲明必須在第一行,之前不能有其他內容);注釋不能嵌套
CDATA區
當XML中一段內容不希望被解析器解析時可以使用CDATA區將其包住,當解析器遇到CDATA區時會將其內容當作文本對待,不會進行解析。語法:<![CDATA[ 內容 ]]>
XML處理指令
處理指令,簡稱PI (processing instruction),處理指令用來指揮解析引擎如何解析XML文檔內容。例如,在XML文檔中可以使用xml-stylesheet指令,通知XML解析引擎,應用css文件顯示xml文檔內容。 <?xml-stylesheet type="text/css" href="1.css"?> 處理指令必須以“<?”作為開頭,以“?>”作為結尾,XML文檔聲明語句就是最常見的一種處理指令。
三、XML約束
在xml技術里,可以編寫一個文檔來約束一個xml文檔的寫法,這稱之為XML約束。作用:約束xml文檔的寫法、對xml進行校驗。常見的XML約束技術:XML DTD 、
XML Schema。
DTD約束
DTD(Document Type Definition),全稱為文檔類型定義。
例:文件清單:book.dtd
1 <!ELEMENT 書架 (書+)> 2 <!ELEMENT 書 (書名,作者,售價)> 3 <!ELEMENT 書名 (#PCDATA)> 4 <!ELEMENT 作者 (#PCDATA)> 5 <!ELEMENT 售價 (#PCDATA)>
文件清單:book.xml
1 <?xml version="1.0" encoding="gb2312"?> 2 <!DOCTYPE 書架 SYSTEM "book.dtd"> 3 <書架> 4 <書> 5 <書名>Java就業培訓教程</書名> 6 <作者>張孝祥</作者> 7 <售價>39.00元</售價> 8 </書> 9 <書> 10 <書名>JavaScript網頁開發</書名> 11 <作者>張孝祥</作者> 12 <售價>28.00元</售價> 13 </書> 14 </書架>
編程校驗XML文檔正確性:默認的情況下IE瀏覽器內置的XML解析器的 約束校驗器是被關閉了的。所以我們需要使用JavaScript手動創建解析器對象,打開約束校驗功能,對XML進行約束校驗。1.創建xml文檔解析器對象 var xmldoc = new ActiveXObject("Microsoft.XMLDOM"); 2.開啟xml校驗 xmldoc.validateOnParse = "true";3.裝載xml文檔 xmldoc.load("book.xml"); 4.獲取錯誤信息 xmldoc.parseError.reason; xmldoc.parseError.line;
引入DTD約束的兩種方式:DTD的約束可以定義在XML文件內部,如果DTD被定義在了XML內部則XML文檔聲明中standalone="yes";DTD的約束也可以定義在一個獨立的 后綴為.dtd的文件中再由xml文件引入,此時引入此dtd的xml文檔聲明中standalone="no"。
XML文件使用 DOCTYPE 聲明語句來指明它所遵循的DTD文件,DOCTYPE聲明語句有兩種形式:
當引用的文件在本地時,采用如下方式: <!DOCTYPE 文檔根結點 SYSTEM "DTD文件的URL">
例如:<!DOCTYPE 書架 SYSTEM “book.dtd”>
當引用的文件是一個公共的文件時,采用如下方式: <!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">
四、DTD語法
元素定義
在DTD文檔中使用ELEMENT聲明一個XML元素,語法格式如下所示: <!ELEMENT 元素名稱 元素約束>
元素約束可以是元素內容、或存放類型
如為元素內容:則需要使用()括起來,如 <!ELEMENT 書架 (書名,作者,售價)> <!ELEMENT 書名 (#PCDATA)>
如為存放類型,則直接書寫,DTD規范定義了如下幾種類型: EMPTY:用於定義空元素,例如<br/> <hr/> ANY:表示元素內容為任意類型。
元素內容中可以使用如下方式,描述內容的組成關系,用逗號分隔,表示內容的出現順序必須與聲明時一致,例如<!ELEMENT MYFILE (TITLE,AUTHOR,EMAIL)>; 用|分隔,表示任選其一,即多個只能出現一個,例如<!ELEMENT MYFILE (TITLE|AUTHOR|EMAIL)>
在元素內容中也可以使用+、*、?等符號表示元素出現的次數:1. +: 一次或多次 (書+);2.?: 0次或一次 (書?);3.*: 0次或多次 (書*)
屬性定義
dtd文檔中的標簽屬性需通過ATTLIST為其設置屬性
語法格式:
<!ATTLIST 元素名 屬性名1 屬性類型 屬性約束 屬性名2 屬性類型 屬性約束 ……>
屬性聲明舉例:
<!ATTLIST 商品 類別 CDATA #REQUIRED 顏色 CDATA #IMPLIED >
對應XML文件:
<商品 類別="服裝" 顏色="黃色">…</商品>
<商品 類別="服裝">…</商品>
屬性類型: CDATA:表示屬性的值是一個普通字符串; ENUMERATED : 屬性的值是一個枚舉列表中的值; ID: 表明屬性的值必須在整個文檔中都是唯一的,如果有重復的id則校驗不通過,ID 屬性的值只能由字母,下划線開始,不能使用數字,不能出現空白字符
屬性約束: #REQUIRED:必須設置該屬性; #IMPLIED:可以設置也可以不設置; #FIXED:說明該屬性的取值固定為一個值,在 XML 文件中不能為該屬性設置其它值。使用該元素時無需為其分配該屬性,XML處理器會自動為給屬性增加固定值;直接使用默認值:在 XML 中可以設置該值也可以不設置該屬性值。若沒設置則使用默認值。
舉例:
<!ATTLIST 頁面作者
姓名 CDATA #IMPLIED
年齡 CDATA #IMPLIED
聯系信息 CDATA #REQUIRED
網站職務 CDATA #FIXED "頁面作者"
個人愛好 CDATA "上網"
>
實體定義
<!ENTITY >,就是對一大段內容的引用,可以簡化代碼的復用
引用實體:在xml中引用的實體叫做引用實體 <!ENTITY 實體名稱 “實體內容” > &實體名稱;
舉例: <!ENTITY copyright “I am a programmer"> …… ©right;
參數實體:在dtd中引用的實體叫做參數實體 <!ENTITY % 實體名稱 "實體內容"> %實體名稱;
舉例1: <!ENTITY % TAG_NAMES "姓名 | EMAIL | 電話 | 地址">
<!ELEMENT 個人信息 (%TAG_NAMES; | 生日)>
<!ELEMENT 客戶信息 (%TAG_NAMES; | 公司名)>
舉例2: <!ENTITY % common.attributes " id ID #IMPLIED account CDATA #REQUIRED " >
... <!ATTLIST purchaseOrder %common.attributes;>
<!ATTLIST item %common.attributes;>
五、XML編程
XML的兩種解析方式:
dom解析:將整個XML使用類似樹的結構保存在內存中,再對其進行操作。是 W3C 組織推薦的處理 XML 的一種方式。需要等到XML完全加載進內存才可以進行操作,耗費內存,當解析超大的XML時慎用。可以方便的對xml進行增刪該查操作
sax解析:逐行掃描XML文檔,當遇到標簽時觸發解析處理器,采用事件處理的方式解析xml,(Simple API for XML) 不是官方標准,但它是 XML 社區事實上的標准,幾乎所有的 XML 解析器都支持它。在讀取文檔的同時即可對xml進行處理,不必等到文檔加載結束,相對快捷。不需要加載進內存,因此不存在占用內存的問題,可以解析超大XML,只能用來讀取XML中數據,無法進行增刪改
使用JAXP進行DOM解析
1.調用 DocumentBuilderFactory.newInstance() 方法得到創建 DOM 解析器的工廠。
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
2.調用工廠對象的 newDocumentBuilder方法得到 DOM 解析器對象。
DocumentBuilder builder = builderFactory.newDocumentBuilder();
3.調用 DOM 解析器對象的 parse() 方法解析 XML 文檔,得到代表整個文檔的 Document 對象,進行可以利用DOM特性對整個XML文檔進行操作了。
Document doc = builder.parse(new File("book.xml"));
DOM模型(document object model):DOM解析器在解析XML文檔時,會把文檔中的所有元素,按照其出現的層次關系,解析成一個個Node對象(節點),並以樹的結構組織起來,存儲到內存中。
JAXP中的文檔對象:1.Document:文檔;2.Element:元素; 3.Attr:屬性; 4.CharacterData:標簽體
Node對象:1.Node是一個接口,代表文檔樹中的單個節點,其他文檔類都是Node接口的實現;2.Node接口上提供了獲取父節點、獲取子節點的方法,由此可以遍歷文檔樹;3.Node接口定義了增刪改查節點方法由此可以修改文檔樹。
參看API:
1 Node getFirstChild() 2 此節點的第一個子節點。 3 Node getLastChild() 4 此節點的最后一個節點。 getAttributes() 5 包含此節點的屬性的 NamedNodeMap(如果它是 Element);否則為 null。 6 Node appendChild(Node newChild) 7 將節點 newChild 添加到此節點的子節點列表的末尾。 8 NodeList getChildNodes() 9 包含此節點的所有子節點的 NodeList。 10 getAttributes() 11 包含此節點的屬性的 NamedNodeMap(如果它是 Element);否則為 null。 12 Node removeChild(Node oldChild) 13 從子節點列表中移除 oldChild 所指示的子節點,並將其返回。 14 Node replaceChild(Node newChild, Node oldChild) 15 將子節點列表中的子節點 oldChild 替換為 newChild,並返回 oldChild 節點。 16 void setTextContent(String textContent) 17 此屬性返回此節點及其后代的文本內容。
對文檔樹的增刪該查只是對內存中的對象進行的操作,如果希望將修改對xml文件起作用,就需要進行XML文檔更新,javax.xml.transform包中的Transformer類用於把代表XML文件的Document對象轉換為某種格式后進行輸出
1.獲取Transformer工廠
TransformerFactory transformerFactory = TransformerFactory.newInstance();
2.獲取Transfomer對象
Transformer transformer = transformerFactory.newTransformer();
3.創建代表輸入和輸出的Source和Result對象
Source source = new DOMSource(doc);
Result result = new StreamResult(new FIle("book.xml"));
4.使用Transformer將 XMLSource 轉換為 Result
transformer.transform(source , Result);
SAX方式解析XML文檔
DOM4J解析XML文檔
1.創建解析器:
SAXReader reader = new SAXReader();
2.利用解析器讀入xml文檔:
Document document = reader.read(new File("input.xml"));
3.獲取文檔的根節點:
Element root = document.getRootElement();
DOM4j節點操作:
1.取得某個節點的子節點. Element element =ele.element(“書名"); List elementList =ele.elements(“書名"); List elementList =ele.elements();
2.獲取節點的名字 node.getName();
3.設置節點的名字 node.setName(String newName);
4.取得節點的文字(標簽體) String text=node.getText();
5.設置節點的文字(標簽體) node.setText("aaa");
6.添加子節點. ele.add(Element e);ele.addElement("age");
7.刪除子節點節點. parentElm.remove(childElm);
8獲取節點類型 node.getNodeType() ;
9.獲取父節點 node.getParent();
10.取得某節點對象的某屬性
Attribute attr= ele.attribute("aaa");
Attribute attr= ele.attribute(0);
List list = ele.attributes();
String value = ele.attributeValue("aaa");
Iterator it = ele.attributeIterator();
11.設置某節點的屬性 ele.add(Attribute attr); ele.addAttribute(name, value); ele.setAttributes(List attrs);
12.刪除某屬性 ele.remove(attribute);
13.在指定位置插入節點:1.得到插入位置的節點列表(list);2.調用list.add(index,elemnent),由index決定element的插入位置。Element元素可以通過DocumentHelper對象得到。
示例代碼:
Element aaa = DocumentHelper.createElement("aaa");
aaa.setText("aaa");
List list = root.element("書").elements();
list.add(1, aaa);
DOM4j屬性:
1.取得屬性的名、值 String name = attribute.getName(); String value = attribute.getValue();
2.設置某屬性的名、值 attribute.setName(); attribute.setValue();
DOM4j字符串和XML的轉換:
1.將字符串轉化為XML
String text = "<members> <member>sitinspring</member></members>";
Document document = DocumentHelper.parseText(text);
2.將文檔或節點的XML轉化為字符串.
String xmlStr = node.asXML();
DOM4j將文檔寫入XML文件
方式一:調用Node提供的write(Writer writer) 方法,使用默認方式將節點輸出到流中:node.write(new FileWriter("book.xml"));
亂碼問題:Dom4j在將文檔載入內存時使用的是文檔聲明中encoding屬性聲明的編碼集進行編碼,如果在此時使用的writer的內部編碼集與最初載入內存時使用的編碼集不同則會出現亂碼問題。FileWriter默認使用操作系統本地碼表即gb2312編碼,並且無法更改。此時可以使用OutputStreamWriter(FileOutputStream("filePath"),"utf-8");的方式自己封裝一個指定碼表的Writer使用,從而解決亂碼問題。
方式二 : 利用XMLWriter寫出Node: XMLWriter writer = new XMLWriter(new FileWriter("output.xml")); writer.write(node); writer.close();
注意:使用這種方式輸出時,XMLWriter首先會將內存中的docuemnt翻譯成UTF-8格式的document,在進行輸出,這時有可能出現亂碼問題。可以使用OutputFormat 指定XMLWriter轉換的編碼為其他編碼。
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK");
XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);
Writer使用的編碼集與文檔載入內存時使用的編碼集不同導致亂碼,使用字節流或自己封裝指定編碼的字符流即可