Digester學(xué)習筆記(一)
在windows下開(kāi)發(fā)程序,用M$提供的接口處理.ini文件或管理注冊表的鍵值是非常方便的。在java平臺上開(kāi)發(fā)程序,則習慣于以xml格式的文件來(lái)存放系統的配置信息,對這種文件的解析和處理,可以用sax或dom。有沒(méi)有更簡(jiǎn)便的方法呢?有,就是用digester模塊。
Digester是Jakarta 子項目Commons下的一個(gè)模塊,支持基于規則的對任意XML文檔的處理。它最初是Structs項目的一部分,后因其通用性而劃歸Commons.
下載及編譯
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
password: anoncvs
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/digester
cd jakarta-commons/digester
ant dist
Digester的運行依賴(lài)下列包:
- 一個(gè)遵循Jaxp(1.1版本及以后)的XML解析器
- Jakarta commons beanutils包(1.5版本及以后)
- Jakarta commons collections包(2.1版本及以后)
- Jakarta commons logging包(1.0.2版本及以后)
一個(gè)簡(jiǎn)單的例子
假定有兩個(gè)JavaBean如下,分別為Foo和Bar
package mypackage;
public class Foo {
public void addBar(Bar bar);
public Bar findBar(int id);
public Iterator getBars();
public String getName();
public void setName(String name);
}
public mypackage;
public class Bar {
public int getId();
public void setId(int id);
public String getTitle();
public void setTitle(String title);
}
用下面的xml文件進(jìn)行配置
<foo name="The Parent">
<bar id="123" title="The First Child"/>
<bar id="456" title="The Second Child"/>
</foo>
用下面幾行代碼即可完成配置文件解析工作:
| Digest解析代碼 | 注釋 |
| Digester digester = new Digester(); | |
| digester.setValidating(false); | 不進(jìn)行XML與相應的DTD的合法性驗證 |
| digester.addObjectCreate("foo", "mypackage.Foo"); | 當遇到<foo>時(shí)創(chuàng )建一個(gè)mypackage.Foo對象,并將其放在棧頂 |
| digester.addSetProperties("foo"); | 根據<foo>元素的屬性(attribute),對剛創(chuàng )建的Foo對象的屬性(property)進(jìn)行設置 |
| digester.addObjectCreate("foo/bar", "mypackage.Bar"); | 當遇到<foo>的子元素<bar>時(shí)創(chuàng )建一個(gè)mypackage.Bar對象,并將其放在棧頂。 |
| digester.addSetProperties("foo/bar"); | 根據<bar>元素的屬性(attribute),對剛創(chuàng )建的Bar對象的屬性(property)進(jìn)行設置 |
| digester.addSetNext("foo/bar", "addBar", "mypackage.Bar"); | 當再次遇到<foo>的子元素<bar>時(shí)創(chuàng )建一個(gè)mypackage.Bar對象,并將其放在棧頂,同時(shí)調用第二棧頂元素(Foo對象)的addBar方法。 |
| Foo foo = (Foo) digester.parse(); | 分析結束后,返回根元素。 |
基本情況
熟悉用SAX來(lái)處理XML文檔的程序員,會(huì )發(fā)現Digester隱藏了遍歷XML元素這些細節,而是提供了更高一層的、更友好的SAX事件接口,從而讓程序員的精力放在對數據的處理過(guò)程中。
使用Digester,須按照以下步驟:
- 創(chuàng )建一個(gè)org.apache.commons.digester.Digester實(shí)例。一個(gè)解析請求完成后,這個(gè)Digester可以被后面復用。但也不要試圖在不同的線(xiàn)程中從共享一個(gè)Digester實(shí)例。
- 根據需要設置一些配置屬性(configuration properties),以控制下一步的解析操作。
- 將一個(gè)或幾個(gè)初始對象(initial object)壓入Digester對象棧,本步驟不是必須的。
- 注冊所有的元素匹配模板(elemet matching pattern)。當一個(gè)模板被從輸入文檔中識別出來(lái)以后,與其相聯(lián)系的處理規則(processing rules)被激活。對一個(gè)特定的模板,可以定義任意多的規則,當識別出該模板后,這些規則依序依次執行。
- 調用digester.parse()方法,一個(gè)XML文檔的引用(用多種方式供選擇)要傳給這個(gè)方法。注意,需要捕捉并處理IOException或SAXEception或處理過(guò)程中拋出的異常。
元素匹配模板
Digester能自動(dòng)遍歷目標XML文檔的元素形成的層次結構,這個(gè)過(guò)程無(wú)需程序員參與。程序員的任務(wù)是決定,在解析的過(guò)程中,當由嵌套的元素形成的一個(gè)特定序列被識別出時(shí),如何處理它。用以描述這種序列的機制,就叫“元素匹配模板”。
具體說(shuō)來(lái),元素和其子元素間,用”/”相隔,如果一些元素前沒(méi)有”/”則其必為根元素。如例:
<a> -- 匹配模板 "a"
<b> -- 匹配模板 "a/b"
<c/> -- 匹配模板 "a/b/c"
<c/> -- 匹配模板 "a/b/c"
</b>
<b> -- 匹配模板 "a/b"
<c/> -- 匹配模板 "a/b/c"
<c/> -- 匹配模板 "a/b/c"
<c/> -- 匹配模板 "a/b/c"
</b>
</a>
字符”*”表示任意級別,如”*/a”表示任意級別的<a>都可匹配(不包括根元素級的).熟悉XLST的朋友,對這種思路一定不陌生。
從上面的描述,可知某個(gè)元素同時(shí)滿(mǎn)足多個(gè)匹配模板是非??赡艿?,在這種情況下,與各個(gè)模板相關(guān)聯(lián)的處理規則(processing rule)的執行順序如下:對begin或body方法,按照各個(gè)rule的注冊順序的先后,對end方法則是注冊順序的反序。
處理規則(processing rule)
元素匹配模板用以識別什么時(shí)候采取行動(dòng),處理規則則用以定義行動(dòng)的內容。
從形式上講,一個(gè)處理規則是一個(gè)java類(lèi),它擴展了org.apache.commons.digester.Rule類(lèi)。每個(gè)處理規則,實(shí)現下列的一個(gè)或幾個(gè)事件處理方法(event method),當相應的模板匹配成功以后,在已定義的某個(gè)時(shí)刻,這些事件方法會(huì )被觸發(fā)。
- begin(),在一個(gè)匹配元素被識別出后的“開(kāi)始”時(shí)刻被調用,這個(gè)元素的所有屬性放在一個(gè)數據結構中被傳遞給begin()
- body(),當元素的嵌套內容(如子元素)被識別出時(shí)被調用。在解析的過(guò)程中,前后的空白被去掉了
- end(),匹配元素的“結束”時(shí)刻被調用。如果子元素也匹配相關(guān)的規則,則這些規則的方法需都執行畢,才能達到該元素的“結束”時(shí)刻。
- finish(),解析結束時(shí)被調用,以提供給各個(gè)規則以清理臨時(shí)數據的機會(huì )。
在設置digester時(shí),通過(guò)調用addRule()方法,來(lái)注冊一個(gè)特定的元素匹配模板以及相應的一個(gè)Rule類(lèi)的實(shí)例。如上所述,Rule類(lèi)中的事件處理方法,會(huì )在適當的時(shí)間被調用。這個(gè)機制,允許動(dòng)態(tài)地生成Rule的實(shí)現。
另外,digester也提供了一些處理常見(jiàn)情況的處理規則類(lèi)。
- ObjectCreateRule,當begin()方法被調用時(shí),這個(gè)規則類(lèi)實(shí)例化一個(gè)指定的java類(lèi),并將其壓入棧頂。這個(gè)被實(shí)例化的類(lèi)的名字,默認是這個(gè)規則類(lèi)構造函數得到的參數,也可以通過(guò)指定正在處理的xml元素的屬性來(lái)傳遞一個(gè)新的類(lèi)的名字。當end()方法被調用 時(shí),棧頂的對象被彈出,Digester中對它的任何引用將被忽略。
- FactoryCreateRule,一個(gè)非常有用的ObjectCreateRule的變體。
- SetPropertiesRule,當begin()方法被調用時(shí),digester使用標準的Java Relection API來(lái)識別JavaBean的屬性設置方法(setter method),這些方法名稱(chēng)中包含屬性(property)的名字,這些屬性與XML元素的屬性(attribute)匹配,于是這些方法被調用并將相應的屬性值(attribute value)傳給它們。這些自然的映射可以被重寫(xiě)。建議不要過(guò)度使用這項功能,在大多數情況下,使用標準的BeanInfo機制會(huì )更好。
- SetPropertyRule,當begin()方法被調用時(shí),digester調用棧頂對象的一個(gè)特定的屬性設置方法(property setter)并傳給它特定的值(property和值分別由兩個(gè)attribute命名)。這對XML需要遵循一個(gè)指定的DTD時(shí)比較有用,你可以設置一個(gè)特別的屬性(property),雖然在指定DTD沒(méi)有attribute與其相對應。
- SetNextRule,當end()方法被調用時(shí),digester分析第二棧頂元素,尋找一個(gè)特定屬性(property)的設置方法(setter method),并接著(zhù)調用這個(gè)方法,以棧頂的元素作參數。這個(gè)規則通常用來(lái)在兩個(gè)對象間建立1對多的關(guān)系,所用的方法也常被叫做addChild什么的。
- SetTopRule,當end()方法被調用時(shí),digester分析棧頂元素,尋找一個(gè)特定屬性(property)的設置方法(setter method),并接著(zhù)調用這個(gè)方法,以第二棧頂的元素作參數。這個(gè)規則通常用來(lái)在兩個(gè)對象間建立1對多的關(guān)系,所用的方法也常被叫做setParent什么的。
- CallMethodRule,這個(gè)規則設置當end()被調用時(shí)執行的棧頂對象的自定義方法,通過(guò)對這個(gè)規則的設置,來(lái)指定方法的名字、參數的數量以及定義的參數類(lèi)型的Java類(lèi)的名字。實(shí)際的參數值,來(lái)自激活這個(gè)方法的元素的子元素。
- CallParamRule,這個(gè)規則用來(lái)指定CallMethodRule的參數的值的來(lái)源,它可以來(lái)自一個(gè)特定的屬性,或子元素的body的內容.
- NodeCreateRule,一個(gè)特殊的規則,將對象樹(shù)的一部分轉換成一個(gè)DOM結點(diǎn)(Node),并壓入棧頂。
對這些標準的規則類(lèi),可以創(chuàng )建它們的實(shí)例,并調用digester.addRule來(lái)注冊它們。由于經(jīng)常使用它們,所以digester定義了一些簡(jiǎn)便的方法來(lái)注冊它們。如:
Rule rule = new SetNextRule(digester, "addChild","com.mycompany.mypackage.MyChildClass");
digester.addRule("a/b/c", rule);
可以用下列代碼替換
digester.addSetNext("a/b/c", "addChild", "com.mycompany.mypackage.MyChildClass");