使
用DOM解析XML文檔時,需要讀入整個XML文檔,然後在記憶體中建立DOM樹,產生DOM樹上的每個節點對象。只有在整個DOM樹建立完畢後,
我們才能做其他的操作,即使我們只需要修改根項目節點的第二個子節點,仍然需要在進行這個小小的修改之間分析整個文檔,在記憶體中構建文檔樹。當XML文檔
比較大時,構建DOM樹將花費大量的時間和記憶體。
一種替代的技術就是使用SAX,SAX允許你在讀取文檔的時候,即對它進行處理,解析完畢處理也就完成了,不必等待整個文檔被分析儲存之後才進行操作。
三步過程
為了使用 XML 檔案中的資訊,必須解析檔案以建立一個 Document 對象。
Document 對象是一個介面(??為了統一嗎
),因而不能直接將它執行個體化;一般情況下,應用程式會相應使用一個工廠。準確的過程因實現而異,但是基本思想是相同的。(同樣,Level 3
標準化了這個任務。)在這個例子 Java 環境中,解析檔案是一個三步過程:
1.建立 DocumentBuilderFactory。 DocumentBuilderFactory 對象建立 DocumentBuilder。
2.建立 DocumentBuilder。 DocumentBuilder 執行實際的解析以建立 Document 對象。
3.解析檔案以建立 Document 對象。
現在您可以開始構建應用程式了。
基本的應用程式
首先建立一個基本的應用程式,即一個名為 OrderProcessor 的類。
『『 『 『
第一步是產生一個DocumentBuilderFactory對象,newInstance()是靜態方法,所以可以直接類名點調用。
第二步是用工廠產生一個DocumentBuilder對象,但是newDocumentBuilder()是抽象方法,還沒實現,在這裡就可以調用了嗎?還是像你以前說的,只要能產生一個抽象類別的對象,那麼這個抽象類別的所以抽象方法就都已經實現了?是這樣嗎
newDocumentBuilder()抽象方法肯定會被非抽象子類實現,這就發生了多態,執行時調用子類的重寫後的方法
view plaincopy to clipboardprint?
public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
......................
}
sun的newInstance()方法
public static DocumentBuilderFactory newInstance() {
try {
return (DocumentBuilderFactory) FactoryFinder.find(
/* The default property name according to the JAXP spec */
"javax.xml.parsers.DocumentBuilderFactory",
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
}
它應該是用反射返回了一個DocumentBuilderFactoryImpl的執行個體,然後用DocumentBuilderFactory強
轉,也就是:DocumentBuilderFactory.newInstance()返回一個Object類型的
DocumentBuilderFactory執行個體,下面的就不用說了吧!
』
view plaincopy to clipboardprint?
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import org.w3c.dom.Document;
public class OrderProcessor {
public static void main (String args[]) {
File docFile = new File("orders.xml");
Document doc = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);
} catch (Exception e) {
System.out.print("Problem parsing the file: "+e.getMessage());
}
}
}
首先,Java 代碼匯入必要的類,然後它建立 OrderProcessor 應用程式。本教程中的例子僅處理一個檔案,因此為簡潔起見,應用程式套件組合含了對該檔案的直接引用。
因此 Document 對象可以在以後使用,應用程式把它定義在 try-catch 塊之外。
在 try-catch 塊中,應用程式建立了 DocumentBuilderFactory,然後再使用它來建立 DocumentBuilder。 最後,DocumentBuilder 解析檔案以建立 Document。
解析器設定
使用 DocumentBuilder 建立解析器的優點之一在於能夠控制 DocumentBuilderFactory 建立的解析器上的各種設定。例如,可以設定解析器驗證文檔:
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);
} catch (Exception e) {
...
Java 的 DOM Level 2 實現允許通過以下方法控制解析器的參數:
setCoalescing():決定解析器是否要將 CDATA 節點轉換為文本,以及是否要和周圍的文本節點合并(如果適用的話)。其預設值為 false。
setExpandEntityReferences(): 確定是否要展開外部實體引用。如果為 true,外部資料將插入文檔。其預設值為 true。(請參閱參考資料以瞭解關於使用外部實體的技巧。)
setIgnoringComments():確定是否要忽略檔案中的注釋。其預設值為 false。
setIgnoringElementContentWhitespace():確定是否要忽略元素內容中的空白(類似於瀏覽器對待 HTML 的方式)。其預設值為 false。
setNamespaceAware():確定解析器是否要注意名稱空間資訊。其預設值為 false。
setValidating():預設情況下,解析器不驗證文檔。將這個參數設定為 true 可開啟驗證功能。
W3C DOM
文件物件模型(DOM)是與平台和語言無關的介面,允許程式和指令碼動態地訪問和更新文檔的內容,結構和樣式。文檔可以進一步處理,處理的結果可以放回到所提供的頁面中。
表3-1 用於處理XML文檔的DOM元素屬性
屬性名稱
描述
childNodes
返回當前元素所有子項目的數組
firstChild
返回當前元素的第一個下級子項目
lastChild
返回當前元素的最後一個子項目
nextSibling
返回緊跟在當前元素後面的元素
nodeValue
指定表示元素值的讀/寫屬性
parentNode
返回元素的父節點
previousSibling
返回緊鄰當前元素之前的元素
表3-2 用於遍曆XML文檔的DOM元素方法
方法名
描述
getElementById(id) (document)
擷取有指定唯一ID屬性值文檔中的元素
getElementsByTagName(name)
返回當前元素中有指定標記名的子項目的數組
hasChildNodes()
返回一個布爾值,指示元素是否有子項目
getAttribute(name)
返回元素的屬性值,屬性由name指定
表3-3 動態建立內容時所用的W3C DOM屬性和方法
屬性/方法
描述
document.createElement(tagName)
文檔對象上的createElement方法可以建立由tagName指定的元素。如果以串div作為方法參數,就會產生一個div元素
Document.createTextNode(text)
文檔對象的createTextNode方法會建立一個包含靜態文本的節點
<element>.appendChild(childNode)
appendChild方法將指定的節點增加到當前元素的子節點列表。例如,可以增加一個option元素,作為select元素的子節點。
<element>.getAttribute(name)
這些方法分別獲得和設定元素中name屬性的值
<element>.setAttribute(name,value)
<element>.insertBefore(newNode,targetNode)
這個方法將節點newNode作為當前元素的子節點插到targetNode元素前面
<element>.removeAttribute(name)
這個方法從元素中刪除屬性name
<element>.removeChild(childNode)
這個方法從元素中刪除子childNode
<element>.replaceChild(newNode,oldNode)
這個方法將節點oldNode替換為節點newNode
<element>.hasChildNodes()
這個方法返回一個布爾值,指示元素是否有子項目
Java和XML是黃金組合,網上已經有很多文章介紹,XML作為電子商務中資料交換,已經有其不可替代的作用,但是在平時系統開發中,我們不一定都用到資料交換,是不是無法使用XML了?
當 然不是,現在已經有一個新趨勢,Java程式的設定檔都開始使用XML格式,以前是使用類似windows的INI格式。(Java中也有
Propertiesy這樣的類專門處理這樣的屬性設定檔)。使用XML作為Java的設定檔有很多好處,從Tomcat的安裝設定檔和J2ee的
設定檔中,我們已經看到XML的普遍應用,讓我們也跟隨流行趨勢用XML武裝起來。
現在關鍵是如何讀取XML設定檔?有好幾種XML解析器:主要有DOM和SAX ,這些區別網上文章介紹很多。
在apache的XML項目組中,目前有Xerces Xalan Cocoon幾個開發XML相關技術的project.Tomcat本身使用的是 Sun 的 JAXP,而其XSL Taglib project中使用Xerces解析器。
好了,上面都是比較煩人的理論問題,還是趕快切入XML的設定檔的讀取吧。
在我們的程式中,通常要有一些根據主機環境確定的變數。比如資料庫訪問使用者名稱和密碼,不同的主機可能設定不一樣。只要更改XML設定檔,就可以正常運行。 localhost
sqlname
username
password
上面這個myenv.xml設定檔一般是放在tomcat的WEB-INF/classes目錄下。
我們編製一個Java程式直接讀取,將dbhost dbuser dbpassword提取出來供其他程式訪問資料庫用。
目 前使用SAX比較的多,與DOM主要區別是
SAX是一行一行讀取XML檔案進行分析,適合比較大檔案,DOM是一次性讀入記憶體,顯然不能對付大檔案.這裡我們使用SAX解析,由於SAX解析器不斷
在發展,網上有不少文章是針對老版本的.如果你使用JDK1.4
,可以參考使用SAX處理XML文檔一文.這裡的程式是根據其改進並且經過實踐調試得來的.。
view plaincopy to clipboardprint?
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import java.util.Properties;
//使用DefaultHandler的好處 是 不必陳列出所有方法,
public class ConfigParser extends DefaultHandler {
////定義一個Properties 用來存放 dbhost dbuser dbpassword的值
private Properties props;
private String currentSet;
private String currentName;
private StringBuffer currentValue = new StringBuffer();
//構建器初始化props
public ConfigParser() {
this.props = new Properties();
}
public Properties getProps() {
return this.props;
}
//定義開始解析元素的方法. 這裡是將 中的名稱xxx提取出來.
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
currentValue.delete(0, currentValue.length());
this.currentName =qName;
}
//這裡是將 之間的值加入到currentValue
public void characters(char[] ch, int start, int length) throws SAXException {
currentValue.append(ch, start, length);
}
//在遇到 結束後,將之前的名稱和值一一對應儲存在props中
public void endElement(String uri, String localName, String qName) throws SAXException {
props.put(qName.toLowerCase(), currentValue.toString().trim());
}
}
上面的這個解析程式比較簡單吧? 其實解析XML就是這麼簡單。
現在我們已經將dbhost dbuser dbpassword的值localhost sqlname username password提取了出來.但是這隻是在在解析器內部,我們的程式還不能訪問.需要再編製一個程式。
view plaincopy to clipboardprint?
import java.util.Properties;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.net.URL;
public class ParseXML{
//定義一個Properties 用來存放 dbhost dbuser dbpassword的值
private Properties props;
//這裡的props
public Properties getProps() {
return this.props;
}
public void parse(String filename) throws Exception {
//將我們的解析器對象化
ConfigParser handler = new ConfigParser();
//擷取SAX工廠對象
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
//擷取SAX解析
SAXParser parser = factory.newSAXParser();
//得到設定檔myenv.xml所在目錄. tomcat中是在WEB-INF/classes
//下例中BeansConstants是用來存放xml檔案中配置資訊的類,可以自己代替或定義
URL confURL = BeansConstants.class.getClassLoader().getResource(filename);
try
{
//將解析器和解析對象myenv.xml聯絡起來,開始解析
parser.parse(confURL.toString(), handler);
//擷取解析成功後的屬性 以後 我們其他應用程式只要調用本程式的props就可以提取出屬性名稱和值了
props = handler.getProps();
}finally{
factory=null;
parser=null;
handler=null;
}
}
}