Digester本來僅僅是Jakarta Struts中的一個工具,用于處理struts-config.xml配置文件。顯然,將XML文件轉(zhuǎn)換成相應(yīng)的Java對象是一項很通用的功能,這個工具理應(yīng)具有更廣泛的用途,所以很快它就在Jakarta Commons項目(用于提供可重用的Java組件庫)中有了一席之地。
簡言之,Digester由"事件"驅(qū)動,通過調(diào)用預(yù)定義的規(guī)則操作對象棧,將XML文件轉(zhuǎn)換為Java對象。工作原理如下:
Digester底層采用SAX解析XML文件,所以很自然的,對象轉(zhuǎn)換由"事件"驅(qū)動,即在識別出特定XML元素時(實際被細分為begin、body、end、finish四個時點),將執(zhí)行特定的動作,比如創(chuàng)建特定的Java對象,或調(diào)用特定對象的方法等。此處的XML元素根據(jù)匹配模式(matching pattern)識別,而相關(guān)操作由規(guī)則(rule)定義。在轉(zhuǎn)換過程中,Digester維持了一個對象棧,可以看作對象轉(zhuǎn)換的工作臺,用來存放轉(zhuǎn)換中生成的、或是為轉(zhuǎn)換臨時創(chuàng)建的Java對象。對輸入XML文件作了一趟完整的掃描后,對象棧的棧頂元素即為目標對象。由于Digester屏蔽了SAX解析的細節(jié),使用者僅需關(guān)注轉(zhuǎn)換操作本身,大大簡化了轉(zhuǎn)換操作。
對使用者而言,Digester的核心在于匹配模式與規(guī)則(matching pattern + rule)。
匹配規(guī)則示例如下:
<a> -- Matches pattern "a"
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
</a>
Digester提供了一些編程中經(jīng)常用到的規(guī)則(rule),以下五類九個rule較為常用:
A:對象創(chuàng)建
1.ObjectCreateRule 當begin()方法被調(diào)用時, 此rule創(chuàng)建相應(yīng)Java對象, 并將其push到Digester的對象棧上。當end()方法被調(diào)用時, 棧頂對象將被pop, Digester內(nèi)所有對該對象的引用都將失效。
2.FactoryCreateRule 創(chuàng)建Java對象的另一種選擇。當待創(chuàng)建的Java對象沒有無參構(gòu)造函數(shù),或需要在創(chuàng)建時需要進行額外的設(shè)置時,需要用此rule。
B:屬性設(shè)置
3.SetPropertiesRule 當begin()方法被調(diào)用時, Digester使用標準的Java反射API,將棧頂對象的屬性設(shè)置為XML元素的同名屬性值。
4.SetPropertyRule 當begin()方法被調(diào)用時, Digester調(diào)用棧頂對象某指定屬性的設(shè)置方法,設(shè)置其值。
C:父子關(guān)系管理
5.SetNextRule 當end()方法被調(diào)用時, Digester將棧頂元素設(shè)置進次棧頂元素中(調(diào)用相應(yīng)的設(shè)置方法)。
6.SetTopRule 當end()方法被調(diào)用時, Digester將次棧頂元素設(shè)置進棧頂元素中(調(diào)用相應(yīng)的設(shè)置方法)。
D:任意方法調(diào)用
7.CallMethodRule 當end()方法被調(diào)用時, Digester將調(diào)用棧頂元素指定名稱的方法。除了方法名外,此rule還需要配置參數(shù)數(shù)目,參數(shù)類型。參數(shù)值一般通過CallParamRule得到。
8.CallParamRule 此rule內(nèi)嵌于CallParamRule中,按順序(相對于0)定義了CallParamRule中參數(shù)值的來源,可選的來源包括當前XML元素的屬性或內(nèi)容。
E:其它
9.NodeCreateRule 將XML文件樹的一部分轉(zhuǎn)換為DOM節(jié)點,并push到Digester的對象棧上。
在基本使用中,使用者通過調(diào)用Digester類的相關(guān)方法,來創(chuàng)建匹配模式與規(guī)則的映射序列。比如,調(diào)用addSetProperties(String pattern),向Digester中加入SetPropertiesRule。
基本步驟如下:
1.創(chuàng)建Digester對象實例。
2.設(shè)置該Digester對象的配置屬性(可選)。
3.將需要的初始對象push到該Digester對象的對象棧上(可選)。
4.需要注冊所有的XML元素匹配模式與處理規(guī)則之間的映射關(guān)系。
5.用digester.parse()解析的XML文檔對象,得到目標對象。
下面是一個簡單示例:
1.foo.xml 數(shù)據(jù)源文件
2.Foo.java 目標Java對象
3.Bar.java 目標Java對象
4.Entry.java 調(diào)用Digester的入口類
【foo.xml】
<?xml version="1.0" encoding="GBK"?>
<foo name="The Parent">
<bar id="123" title="The First Child"/>
<bar id="456" title="The Second Child"/>
</foo>
【Foo.java】
package org.easev.digester;
import java.util.HashMap;
import java.util.Iterator;
public class Foo {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
private HashMap bars = new HashMap();
public void addBar(Bar bar) {
bars.put(String.valueOf(bar.getId()), bar);
}
public Bar findBar(int id) {
return (Bar) bars.get(String.valueOf(id));
}
public Iterator getBars() {
return bars.keySet().iterator();
}
}
【Bar.java】
package org.easev.digester;
public class Bar {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
【Entry.java】
package org.easev.digester;
import java.io.File;
import java.util.Iterator;
import org.apache.commons.digester.Digester;
public class Entry {
public static void main(String[] args) throws Exception {
//相對路徑定義與包名相關(guān)
File input = new File("org/easev/digester/foo.xml");
Digester digester = new Digester();
digester.setValidating(false);
//完整類名定義,包名改變時需做相應(yīng)變化
digester.addObjectCreate("foo", "org.easev.digester.Foo");
digester.addSetProperties("foo");
digester.addObjectCreate("foo/bar", "org.easev.digester.Bar");
digester.addSetProperties("foo/bar");
digester.addSetNext("foo/bar", "addBar", "org.easev.digester.Bar");
Foo foo = (Foo) digester.parse(input);
//測試裝載是否成功
Iterator iter = foo.getBars();
while (iter.hasNext()) {
System.out.println((String) iter.next());
}
}
}
匹配模式?jīng)]有什么文章好做,那么下面要討論的就必然是規(guī)則了。在基本的使用方式下,Digester雖然使用XML文件定義Java對象的狀態(tài),提高了系統(tǒng)的靈活性,但是匹配模式與規(guī)則的映射序列(裝載邏輯)仍然通過硬編碼來定義,這種方式不易修改與重用。所以Digester還提供了一種高級的使用方式,用一個XML文件定義Java對象的狀態(tài)(數(shù)據(jù)源文件),用另一個XML文件定義裝載數(shù)據(jù)源文件的裝載邏輯。
這樣,對象的裝載過程分成了兩步:
1.裝載邏輯的"裝載",其結(jié)果表現(xiàn)為定義了rule的Digester;
2.根據(jù)上一步得到的Digester,裝載目標對象。
套用上面的一個例子,增加了rule.xml,并改寫了Entry.java
【rule.xml】
<?xml version=‘1.0‘?>
<!DOCTYPE digester-rules
PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN"
"file:///../digester/dtds/digester-rules.dtd">
<digester-rules>
<pattern value="foo">
<object-create-rule classname="org.easev.digester.Foo"/>
<set-properties-rule/>
<pattern value="bar">
<object-create-rule classname="org.easev.digester.Bar"/>
<set-properties-rule/>
<set-next-rule methodname="addBar"/>
</pattern>
</pattern>
</digester-rules>
【Entry.java】
package org.easev.digester;
import java.io.File;
import java.util.Iterator;
import org.apache.commons.digester.Digester;
public class Entry {
public static void main(String[] args) throws Exception {
//相對路徑定義與包名相關(guān)
File data = new File("org/easev/digester/foo.xml");
File rule = new File("org/easev/digester/rule.xml");
Digester digester = DigesterLoader.createDigester(rule.toURL());
Foo foo = (Foo) digester.parse(data);
//測試裝載是否成功
Iterator iter = foo.getBars();
while (iter.hasNext()) {
System.out.println((String) iter.next());
}
}
}
我們可以看到,使用Digester的代碼變得相當簡潔,而要付出的代價就是為裝載邏輯寫一個配置文件。
除了Digester之外,當然還有其它的方法來實現(xiàn)Java對象的綁定與裝載:
1.java.util.Properties,簡單的配置屬性(比如數(shù)據(jù)庫連接信息),可以寫在properties文件中,調(diào)用Properties對象的load(InputStream)方法將配置中的健值對加載到Properties對象中。這種方式一般僅適用于簡單的配置信息的加載。
2.JAXB,Java Architecture for XML Binding,在Java Web Services Developer Pack V 1.1中提供了一個參考實現(xiàn)。使用這種方式時,除了提供數(shù)據(jù)源XML文件之外,還必須提供相應(yīng)的Schema文件。加載前,首先用Binding Compiler將Schema轉(zhuǎn)換得到目標Java類的接口與實現(xiàn),然后再調(diào)用Unmarshaller或Marshaller將數(shù)據(jù)源XML文件信息加載到Java對象中,或?qū)⒃O(shè)置的Java對象數(shù)據(jù)導(dǎo)出為XML文件。
3.XPath
4.JaxMe