1. 程式人生 > >java讀取xml配置檔案(小結)

java讀取xml配置檔案(小結)

使用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()抽象方法肯定會被非抽象子類實現,這就發生了多型,執行時呼叫子類的重寫後的方法



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例項,下面的就不用說了吧!






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文件一文.這裡的程式是根據其改進並且經過實踐除錯得來的.。

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提取了出來.但是這只是在在解析器內部,我們的程式還不能訪問.需要再編制一個程式。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;
}
}
}