Digester學(xué)習(xí)筆記(一)
在windows下開發(fā)程序,用M$提供的接口處理
Digester是Jakarta 子項(xiàng)目Commons下的一個(gè)模塊,支持基于規(guī)則的對(duì)任意XML文
下載及編譯
password: anoncvs
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/digester
cd jakarta-commons/digester
ant dist
Digester的運(yùn)行依賴下列包:
- 一個(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和Barpublic 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)行配置
??<bar id="123" title="The First Child"/>
??<bar id="456" title="The Second Child"/>
</foo>
用下面幾行代碼即可完成配置文件解析工作:
Digest解析代碼 | 注釋 |
Digesterdigester = new Digester(); | |
digester.setValidating(false); | 不進(jìn)行XML與相應(yīng)的DTD的合法性驗(yàn)證 |
digester.addObjectCreate("foo", "mypackage.Foo"); | 當(dāng)遇到<foo>時(shí)創(chuàng)建一個(gè)mypackage.Foo對(duì)象,并將其放在棧頂 |
digester.addSetProperties("foo"); | 根據(jù)<foo>元素的屬性(attribute) |
digester.addObjectCreate("foo/bar", "mypackage.Bar"); | 當(dāng)遇到<foo>的子元素<bar> 時(shí)創(chuàng)建一個(gè)mypackage.Bar對(duì)象,并將其放在棧頂。 |
digester.addSetProperties("foo/bar"); | 根據(jù)<bar>元素的屬性(attribute) |
digester.addSetNext("foo/bar", "addBar", "mypackage.Bar "); | 當(dāng)再次遇到<foo>的子元素<bar>時(shí)創(chuàng)建一個(gè)mypackage.Bar對(duì)象,并將其放在棧頂,同時(shí)調(diào)用第二棧頂元素(Foo對(duì)象 |
Foo foo = (Foo) digester.parse(); | 分析結(jié)束后,返回根元素。 |
基本情況
熟悉用SAX來處理XML文檔的程序員,會(huì)發(fā)現(xiàn)Digester隱藏了遍歷XML元素這些細(xì)節(jié),而是提供了更高一層的使用Digester,須按照以下步驟:
- 創(chuàng)建一個(gè)org.apache.commons.digester.Digester實(shí)例。一個(gè)解析請(qǐng)求完成后,這個(gè)Digester可以被后面復(fù)用。但也不要試圖在不同的線程中從共享一個(gè) Digester實(shí)例。
- 根據(jù)需要設(shè)置一些配置屬性(configuration properties),以控制下一步的解析操作。
- 將一個(gè)或幾個(gè)初始對(duì)象(initial object)壓入Digester對(duì)象棧,本步驟不是必須的。
- 注冊(cè)所有的元素匹配模板(elemet matching pattern)。當(dāng)一個(gè)模板被從輸入文檔中識(shí)別出來以后
,與其相聯(lián)系的處理規(guī)則(processing rules)被激活。對(duì)一個(gè)特定的模板,可以定義任意多的規(guī)則 ,當(dāng)識(shí)別出該模板后,這些規(guī)則依序依次執(zhí)行。 - 調(diào)用digester.parse()方法,一個(gè)XML文檔的引用(用多種方式供選擇
)要傳給這個(gè)方法。注意,需要捕捉并處理IOException或 SAXEception或處理過程中拋出的異常。 - 調(diào)用digester.parse()方法,一個(gè)XML文檔的引用(用多種方式供選擇
元素匹配模板
Digester能自動(dòng)遍歷目標(biāo)XML文檔的元素形成的層次結(jié)構(gòu),這個(gè)過程無需程序具體說來,元素和其子元素間,用"/"相隔,如果一些元素前沒有
??<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>
字符"*"表示任意級(jí)別,如"*/a"表示任意級(jí)別的<a
從上面的描述,可知某個(gè)元素同時(shí)滿足多個(gè)匹配模板是非??赡艿?wbr>,在這種情況下,與各個(gè)模板相關(guān)聯(lián)的處理規(guī)則(processin
處理規(guī)則(processing rule)
元素匹配模板用以識(shí)別什么時(shí)候采取行動(dòng),處理規(guī)則則用以定義行動(dòng)的從形式上講,一個(gè)處理規(guī)則是一個(gè)java類,它擴(kuò)展了org
- begin(),在一個(gè)匹配元素被識(shí)別出后的"開始"時(shí)刻被調(diào)用
,這個(gè)元素的所有屬性放在一個(gè)數(shù)據(jù)結(jié)構(gòu)中被傳遞給begin() - body(),當(dāng)元素的嵌套內(nèi)容(如子元素)被識(shí)別出時(shí)被調(diào)用
。在解析的過程中,前后的空白被去掉了 - end(),匹配元素的"結(jié)束"時(shí)刻被調(diào)用。如果子元素也匹配相關(guān)
的規(guī)則,則這些規(guī)則的方法需都執(zhí)行畢,才能達(dá)到該元素的"結(jié)束 "時(shí)刻。 - finish(),解析結(jié)束時(shí)被調(diào)用,以提供給各個(gè)規(guī)則以清理臨時(shí)
數(shù)據(jù)的機(jī)會(huì)。 - body(),當(dāng)元素的嵌套內(nèi)容(如子元素)被識(shí)別出時(shí)被調(diào)用
在設(shè)置digester時(shí),通過調(diào)用addRule()方法,來注冊(cè)一個(gè)特定的元素匹配模
另外,digester也提供了一些處理常見情況的處理規(guī)則類。
- ObjectCreateRule,當(dāng)begin(
)方法被調(diào)用時(shí),這個(gè)規(guī)則類實(shí)例化一個(gè)指定的java類 ,并將其壓入棧頂。這個(gè)被實(shí)例化的類的名字,默認(rèn)是這個(gè)規(guī)則類構(gòu)造函數(shù)得到的參數(shù) ,也可以通過指定正在處理的xml元素的屬性來傳遞一個(gè)新的類的名 字。當(dāng)end()方法被調(diào)用 時(shí),棧頂?shù)膶?duì)象被彈出,Digester中對(duì)它的任何引用將被忽略。 - FactoryCreateRule,一個(gè)非常有用的Object
CreateRule的變體。 - SetPropertiesRule,當(dāng)begin(
)方法被調(diào)用時(shí),digester 使用標(biāo)準(zhǔn)的Java Relection API來識(shí)別JavaBean的屬性設(shè)置方法(setter method),這些方法名稱中包含屬性(property )的名字,這些屬性與XML元素的屬性(attribute )匹配,于是這些方法被調(diào)用并將相應(yīng)的屬性值(attribute value)傳給它們。這些自然的映射可以被重寫 。建議不要過度使用這項(xiàng)功能,在大多數(shù)情況下,使用標(biāo)準(zhǔn)的Bean Info機(jī)制會(huì)更好。 - SetPropertyRule,當(dāng)begin()方法被調(diào)用時(shí),digester調(diào)用棧頂對(duì)象的一個(gè)特定的屬性設(shè)置方法(property setter)并傳給它特定的值(property和值分別由兩個(gè)
attribute命名)。這對(duì)XML需要遵循一個(gè)指定的DTD時(shí) 比較有用,你可以設(shè)置一個(gè)特別的屬性(property),雖然在指定DTD沒有att ribute與其相對(duì)應(yīng)。 - SetNextRule,當(dāng)end()方法被調(diào)用時(shí),digester分析第二棧頂元素,尋找一個(gè)特定屬性(property
)的設(shè)置方法 (setter method),并接著調(diào)用這個(gè)方法,以棧頂?shù)脑刈鲄?shù) 。這個(gè)規(guī)則通常用來在兩個(gè)對(duì)象間建立1對(duì)多的關(guān)系 ,所用的方法也常被叫做addChild什么的。 - SetTopRule,當(dāng)end()方法被調(diào)用時(shí),digester分析棧頂元素,尋找一個(gè)特定屬性(property)的設(shè)置方法 (setter method),并接著調(diào)用這個(gè)方法,以第二棧頂?shù)脑刈鲄?shù)
。這個(gè)規(guī)則通常用來在兩個(gè)對(duì)象間建立1對(duì)多的關(guān)系 ,所用的方法也常被叫做setParent 什么的。 - CallMethodRule,這個(gè)規(guī)則設(shè)置當(dāng)end(
)被調(diào)用時(shí)執(zhí)行的棧頂對(duì)象的自定義方法,通過對(duì)這個(gè)規(guī)則的設(shè)置 ,來指定方法的名字、參數(shù)的數(shù)量以及定義的參數(shù)類型的Java類的 名字。實(shí)際的參數(shù)值,來自激活這個(gè)方法的元素的子元素。 - CallParamRule,這個(gè)規(guī)則用來指定CallMetho
dRule的參數(shù)的值的來源,它可以來自一個(gè)特定的屬性 ,或子元素的body的內(nèi)容. - NodeCreateRule,一個(gè)特殊的規(guī)則,將對(duì)象樹的一部分
轉(zhuǎn)換成一個(gè)DOM結(jié)點(diǎn)(Node),并壓入棧頂。 - FactoryCreateRule,一個(gè)非常有用的Object
對(duì)這些標(biāo)準(zhǔn)的規(guī)則類,可以創(chuàng)建它們的實(shí)例,并調(diào)用digester.addRule來注冊(cè)它們。由于經(jīng)常使用它們,所以digest
digester.addRule("a/b/c", rule);
Digester學(xué)習(xí)筆記(二)
為便于理解,將筆記的內(nèi)容結(jié)構(gòu)作了一些調(diào)整。
對(duì)象棧
對(duì)digester技術(shù)最普通的應(yīng)用,是用來動(dòng)態(tài)創(chuàng)建一個(gè)由Java對(duì)象構(gòu)成的樹結(jié)構(gòu)- clear(),清空棧的內(nèi)容
- peek(),返回對(duì)棧頂對(duì)象的引用
- pop(),將棧頂對(duì)象彈出并返回
- push(),將一個(gè)新的對(duì)象壓入棧頂
用棧的原因,就是當(dāng)識(shí)別出一個(gè)XML元素的"開始"時(shí)
如何描述對(duì)象間的關(guān)系呢?將棧頂?shù)膶?duì)象做為一個(gè)參數(shù)
如果取得生成的第一個(gè)對(duì)象呢?可以讓parse()方法返回
日志(logging)
日志是一個(gè)調(diào)試Digester規(guī)則集的非常重要的工具,它可以記錄非常豐富的信息,因它在使用Digester之前有必要了解日志是如何工作的。
Digester使用Jakarta Commons Logging,這個(gè)模塊并不是具體的日志實(shí)現(xiàn),而只是一個(gè)可設(shè)置
Digester主要使用兩個(gè)記錄器:
- SAX相關(guān)的信息,被送往org.apache.commons.digester.Digester.sax記錄器,記錄了Digester收到的SAX的事件的信息。
- 其它的所有信息,都被送往org.apache.commons. digester.Digester記錄器,這個(gè)記錄器在調(diào)試Digester時(shí)打開而在產(chǎn)品中常將其
關(guān)閉
假定用commons logging自帶的基本日志工具,并以DEBUG級(jí)別記錄Dig
Digester包中的例子
<address-book>
??<person?id="1"?category=
????<name>Gonzo</name>
????<email?type="business"> gonzo@muppets.com</email>
????<gender?result="the?whole
??</person>
??<person?id="2"?category=
????<name>Kermit</name>
????<email?type="business">kermit@muppets.com</email>
????<email?type="home">kermie@acme.com</email>
??</person>
</address-book>**********Person.java************
import?java.util.HashMap;
import?java.util.Iterator;
public?class?Person?{
??private?int?id;
??private?String?category;
??private?String?name;
??private?HashMap?emails?=?new
??//下面的兩個(gè)方法的名字中set以后的部分,與
??public?void?setId(int?id)?{
??????this.id?=?id;
??}
??public?void?setCategory
??????this.category?=?category;
??}
? ?//對(duì)name而言,因?yàn)槠渲祦碜?lt;name>標(biāo)簽的內(nèi)容而非屬
??public?void?setName(String
??????this.name?=?name;
??}
??//同name,此時(shí)還要一一指定addEmail的參數(shù)值的
??public?void?addEmail(String
??????emails.put(type,?address);
??}
??public?void?print()?{
??????System.out.println("Person?#"?+?id);
??????System.out.println("??category="?+?category);
??????System.out.println ("??name="?+?name);
??????for(Iterator?i?=?emails.keySet().iterator();?i.hasNext();?)?{
??????????String?type?=?
??????????String?address?=?
??????????System.out.println("??email?(type?"?+?type?+?")?
??????}
??}
}
**********AddressBook.java***********
import?java.util.LinkedList;
import?java.util.Iterator;
public?class?AddressBook?{
????LinkedList?people?=?new
????public?void?addPerson
????????people.addLast(p);
????}
????public?void?print()?{
????????System.out.println("Address?book?has?"?+?people.size()?+?"?entries");
????????for(Iterator?i?=?people.iterator();?i.hasNext();?)?{
????????????Person?p?=?(Person
????????????p.print();
????????}
????}
}
************AddressBookDigester
import?org.apache.commons.digester.Digester;
/**
?*?Usage:?java?Example1?example.xml
?*/
public?class?AddressBookDigeste
????public?static?void?main
????????if?(args.length?!=?1)?{
????????????usage();
????????????System.exit(-1);
????????}
????????String?filename?=?args
????????//?創(chuàng)建一個(gè)Digester實(shí)例
????????Digester?d?=?new?Digester();
????????//?創(chuàng)建AddressBook實(shí)例
????????AddressBook?book?=?new
????????d.push(book);
????????//?增加規(guī)則
????????addRules(d);
????????//?處理輸入的xml文件
????????try?{
????????????java.io.File?srcfile?=?new?java.io.File(filename);
????????????d.parse(srcfile);
????????}
????????catch(java.io.IOException?ioe)?{
????????????System.out.println("Error?reading?input?file:"?+?ioe.getMessage());
????????????System.exit(-1);
????????}
????????catch(
org.xml.sax.SAXException?se)?{
????????????System.out.println("Error?parsing?input?file:"?+?se.getMessage());
????????????System.exit(-1);
????????}
????????
????????
????????//?將解析出的地址數(shù)據(jù)打印出來
????????book.print();
????}
????
????private?static?void
????????//?當(dāng)遇到<person>時(shí)
????????d.addObjectCreate("address-book/person",?Person.class);
????????
????????//?將<person>標(biāo)簽的屬性
????????//?如果某標(biāo)簽屬性沒法通過名字找到相應(yīng)的屬性設(shè)置方法,則此標(biāo)簽屬性被忽略(如example.xml中第一個(gè)<person>的try屬性)。
????????d.addSetProperties("address-book/person");
????????//?調(diào)用第二棧頂對(duì)象(AddressBook
????????d.addSetNext("address-book/person",?
????????
????????//?當(dāng)遇到<person>的子元素
????????//?此處addCallMethod方法的第一參數(shù)是規(guī)則,第二個(gè)參數(shù)是方法的名字,第三個(gè)是參數(shù)的數(shù)量
????????d.addCallMethod("address-book/person/name",?
????????
????????//?當(dāng)遇到<person>的子元素
????????//?此處addCallParam方法的第一參
????????d.addCallMethod("address-book/person/email",?
????????d.addCallParam("address-book/person/email",
????????d.addCallParam("address-book/person/email",
????}
????private?static?void?usage(
????????System.out.println("Usage:?java?Example1?example.xml");
????}
}
Person?#1
??category=acquaintance
??name=Gonzo
??email?(type?business)?:?gonzo@muppets.com
Person?#2
??category=rolemodel
??name=Kermit
??email?(type?business)?:?kermit@muppets.com
??email?(type?home)?:?kermie@acme.com
Digester學(xué)習(xí)筆記(三)
總覺得,Digester不僅僅能作配置文件解析,而且可以作得更多。
配置屬性
Digester用來解析應(yīng)用系統(tǒng)的配置文件,其本身也有很可配置的屬性。屬性 | 描述 |
classLoader | 指定類裝載器(class loader)。ObjectCreateRule 和 FactoryCreateRule兩個(gè)規(guī)則中,需要?jiǎng)討B(tài)加載一些 |
errorHandler | 指定 SAX ErrorHandler,以在出現(xiàn)此類錯(cuò)誤時(shí)調(diào)用。默認(rèn)情況下 |
namespaceAware |
一個(gè)布爾值,為真時(shí)對(duì)XML文件的解析時(shí)會(huì)考慮元素的域名空間 |
ruleNamespaceURI | 指定后續(xù)加入的規(guī)則所屬的命名空間,如果此值為null |
rules | 設(shè)定規(guī)則模板與XML元素的匹配處理程序。由于這個(gè)匹配程序是插件 |
useContextClassLoader | 一個(gè)布爾值,為真時(shí)FactoryCreateRule 和 ObjectCreateRule 兩個(gè)規(guī)則中對(duì)類的裝載將會(huì)采用當(dāng)前線程上下文中指定的加載器 |
validating | 一個(gè)布爾值,為真時(shí)解析器會(huì)根據(jù)DTD內(nèi)容對(duì)XML文檔進(jìn)行合法性 |
除了上述屬性外,還可以注冊(cè)一個(gè)本地DTD,以供DOCTYPE聲
例如,Struect框架控制器中,使用下述的注冊(cè)
digester.register("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",url.toString());
規(guī)則集打包
通常情況下,一個(gè)規(guī)則被創(chuàng)建后,接著便注冊(cè),然后等在event時(shí)
??public?MyRuleSet()?{
????this("");
??}
??public?MyRuleSet(String
????super();
????this.prefix?=?prefix;
????this.namespaceURI ?=?"http://www.mycompany.com
??}
??protected?String?prefix?=
??public?void?addRuleInstances(Digester?digester)?{
????digester.addObjectCreate(prefix?+?"foo
??????"com.mycompany.MyFoo");
????digester.addSetProperties(prefix?+?
??}
}
... 一些配置Digester ...
digester.addRuleSet(new MyRuleSet("baz/"));
帶命名空間的XML解析
這種情況下,使用Digester的步驟為:
- 在Digester初始化部分,指明要考慮命名空間。
digester.setNamespaceAware(true);
- 指明一些規(guī)則的命名空間,如
digester.setRuleNamespaceURI(" http://www.mycompany.com
/MyNamespace") ; - 接下來定義一些與此命名空間有關(guān)的規(guī)則,此時(shí)可以省卻前綴,如
digester.addObjectCreate("foo/bar", "com.mycompany.MyFoo");
digester.addSetProperties("foo/bar"); - 對(duì)其它命名空間,重復(fù)前面的2步
另外,在指明要digester考慮命名空間之后,在定義匹配模板時(shí),可以將命名空間別名加":
開發(fā)定制的匹配處理過程
通過實(shí)現(xiàn) org.apache.commons.digester.Rules接口或擴(kuò)展org.apache.commons.digester.RulesBase類來達(dá)到定制匹配過程的目的。
Digester提供ExtendedBaseRules來擴(kuò)展了匹配模板的定義
一些認(rèn)識(shí)
通過看說明材料,尤其在學(xué)習(xí)Digester包中的Catalog例子以后,有一些認(rèn)識(shí):
1、由于xml對(duì)屬性名字的定義要求,與Java中對(duì)方法名字的定
2、對(duì)于根元素,與其子元素建立聯(lián)系,有幾種辦法
3、如果某對(duì)象類構(gòu)造都要參數(shù),則此時(shí)需要擴(kuò)展AbstractO
4、設(shè)有某個(gè)標(biāo)簽,要想自動(dòng)用該標(biāo)簽子元素的內(nèi)容填充該標(biāo)簽對(duì)應(yīng)的
5、如果對(duì)象的屬性是整型,則Digester自動(dòng)將xml文件中字符串值轉(zhuǎn)換為整型。
6、在指明要digester考慮命名空間之后,如果不會(huì)引起歧義,完全可以忽略命名空間的存在