指定模式和規(guī)則
Digester框架以模式(Pattern)和規(guī)則(Rule)為基礎(chǔ)處理輸入的XML,。模式必須與XML元素匹配,,包括其名字和在文檔樹內(nèi)的位置。描述匹配模式的語法類似于XPath匹配模式,,例如:catalog模式匹配頂層的<catalog>元素,,catalog/book模式匹配直接嵌套在<catalog>元素內(nèi)的<book>元素(但不匹配文檔內(nèi)其他位置的<book>元素)。
所有的模式都必須指定其完整名稱——從根元素開始的完整路徑,。唯一的例外是包含通配符(“*”)的模式,,例如*/name模式匹配XML文檔內(nèi)任何位置的<name>元素。但是根元素不必特別指出,因為所有的路徑都是從根元素開始的絕對路徑,。
當(dāng)Digester發(fā)現(xiàn)一個指定的模式,,它就執(zhí)行關(guān)聯(lián)的任務(wù)。由此可見,,Digester框架顯然與SAX解析器有著密切的關(guān)系(實際上,,Digester類實現(xiàn)了org.xml.sax.ContentHandler,并維護著解析棧),。所有在Digester中使用的規(guī)則必須擴展org.apache.commons.digester.Rule,,后者本身提供了一些類似于SAX的ContentHandler回調(diào)函數(shù)的方法。例如,,當(dāng)遇到匹配元素的開始標(biāo)記和結(jié)束標(biāo)記時,,begin()方法和end()方法將分別被調(diào)用。
一旦遇到匹配元素的內(nèi)容,,body()方法被調(diào)用,;最后被調(diào)用的方法是finish(),這個方法在匹配元素的結(jié)束標(biāo)記處理完畢之后被調(diào)用,,用來執(zhí)行可能需要的事后清理任務(wù),。然而,大多數(shù)時候我們不必關(guān)注這些方法,,因為框架提供的標(biāo)準(zhǔn)規(guī)則很可能已經(jīng)提供了所有必需的功能,。
要反配制一個文檔,首先創(chuàng)建一個org.apache.commons.digester.Digester類的實例,,如果必要的話,,進行一些配置操作,指定必需的模式和規(guī)則,,最后向parse()方法傳遞一個XML文件的引用。下面的DigesterDriver示范了這一處理過程(必須在命令行上指定輸入XML文檔的名稱),。
import org.apache.commons.digester.*;
import java.io.*;
import java.util.*;
public class DigesterDriver {
public static void main( String[] args ) {
try {
Digester digester = new Digester();
digester.setValidating( false );
digester.addObjectCreate( "catalog", Catalog.class );
digester.addObjectCreate( "catalog/book", Book.class );
digester.addBeanPropertySetter( "catalog/book/author", "author" );
digester.addBeanPropertySetter( "catalog/book/title", "title" );
digester.addSetNext( "catalog/book", "addBook" );
digester.addObjectCreate( "catalog/magazine", Magazine.class );
digester.addBeanPropertySetter( "catalog/magazine/name", "name" );
digester.addObjectCreate( "catalog/magazine/article", Article.class );
digester.addSetProperties( "catalog/magazine/article", "page", "page" );
digester.addBeanPropertySetter( "catalog/magazine/article/headline" );
digester.addSetNext( "catalog/magazine/article", "addArticle" );
digester.addSetNext( "catalog/magazine", "addMagazine" );
File input = new File( args[0] );
Catalog c = (Catalog)digester.parse( input );
System.out.println( c.toString() );
} catch( Exception exc ) {
exc.printStackTrace();
}
}
}
|
在上面的代碼中,,我們首先創(chuàng)建了Digester類的一個實例digester,然后指定它不要用DTD驗證XML文檔的合法性——這是因為我們沒有為XML文檔定義DTD,。接下來,,我們指定了模式和關(guān)聯(lián)的規(guī)則:ObjectCreateRule創(chuàng)建指定類的一個實例,并將它壓入解析棧,。SetPropertiesRule把Bean屬性設(shè)置成當(dāng)前XML元素的屬性值——規(guī)則的第一個參數(shù)是XML屬性的名稱,,第二個參數(shù)是Bean屬性的名稱。
SetPropertiesRule獲取的是XML屬性的值,,而BeanPropertySetterRule獲取的是位于當(dāng)前元素內(nèi)的原始字符數(shù)據(jù)值,。使用BeanPropertySetterRule時不必指定要設(shè)置的Bean屬性名字,默認是當(dāng)前XML元素的名稱。在上面的例子中,,在匹配catalog/magazine/article/headline模式的規(guī)則定義中使用的就是默認值,。最后,SetNextRule彈出解析棧頂部的對象,,并把該對象傳遞給它下面對象的指定名稱的方法——通常用來把一個配置完畢的Bean插入父對象,。
注意,我們可以為同一個模式注冊多個規(guī)則,。如果注冊了多個規(guī)則,,則這些規(guī)則按照它們被加入到Digester的次序執(zhí)行,例如,,如果要處理catalog/magazine/article的元素,,我們首先創(chuàng)建合適的article Bean,然后設(shè)置page屬性,,最后彈出完成后的article Bean,,并把它插入magazine。
調(diào)用任意方法
我們不僅可以設(shè)置Bean的屬性,,而且還可以調(diào)用堆棧內(nèi)對象的任意方法,。這通過CallMethodRule完成,我們只需指定方法名字,,如有必要,,再說明調(diào)用的參數(shù)類型和數(shù)量。CallParamRule用來定義傳遞給被調(diào)用函數(shù)的參數(shù)值,,參數(shù)值可以從當(dāng)前XML元素的命名的屬性獲取,,也可以從當(dāng)前元素包含的原始字符數(shù)據(jù)獲取。例如,,在前面實現(xiàn)DigesterDriver的例子中,,我們可以不用BeanPropertySetterRule,而是通過顯式調(diào)用屬性的set方法達到同樣的目的:
digester.addCallMethod( "catalog/book/author", "setAuthor", 1 );
digester.addCallParam( "catalog/book/author", 0 );
|
上面的第一行代碼給出了要調(diào)用的方法(即setAuthor()),,以及該調(diào)用需要的參數(shù)數(shù)量(即1),。第二行代碼的意思是從元素包含的字符數(shù)據(jù)獲取函數(shù)參數(shù)的值,把它作為參數(shù)數(shù)組的第一個傳入(即索引是0的數(shù)組元素),。如果我們指定了XML元素屬性的名稱(例如digester.addCallParam( "catalog/book/author", 0, "author" );),,則參數(shù)值將從當(dāng)前元素的相應(yīng)屬性值獲取。
這里必須注意的是,,“digester.addCallMethod( "pattern", "methodName", 0 );”這個語句不是指定了一個不帶參數(shù)的方法調(diào)用,,而是指定了帶有一個參數(shù)的方法調(diào)用,它的值就是當(dāng)前XML元素的字符數(shù)據(jù),!這樣,,我們又有了另一種替代BeanPropertySetterRule的辦法:
digester.addCallMethod( "catalog/book/author", "setAuthor", 0 );
|
如果要調(diào)用一個確實沒有參數(shù)的方法,,必須采用如下形式:digester.addCallMethod( "pattern", "methodName" );。
標(biāo)準(zhǔn)規(guī)則概要
下面簡要說明所有標(biāo)準(zhǔn)規(guī)則,。
創(chuàng)建
ObjectCreateRule:利用指定類的默認構(gòu)造函數(shù),,創(chuàng)建該類的一個對象,并把對象壓入棧,。當(dāng)元素處理結(jié)束時,,對象被彈出。被實例化的類可通過class對象或類的全稱給出,。
FactoryCreateRule:利用指定的工廠類創(chuàng)建一個對象,,把對象壓入棧。對于沒有提供默認構(gòu)造函數(shù)的類,,這一規(guī)則很有用,。用于該規(guī)則的工廠類必須實現(xiàn)org.apache.commons.digester.ObjectCreationFactory接口。
設(shè)置屬性
SetPropertiesRule:利用指定名稱的XML元素屬性值,,設(shè)置頂層Bean的一個或者多個指定名稱的屬性,。XML元素的屬性名稱和Bean的屬性名稱以String[]數(shù)組形式傳入該規(guī)則(通常用來處理之類的結(jié)構(gòu))。
BeanPropertySetterRule:把頂層Bean的指定名稱的屬性設(shè)置成當(dāng)前XML元素包含的字符數(shù)據(jù),。(通常用來處理<page>10</page>之類的結(jié)構(gòu)),。
SetPropertyRule:設(shè)置頂層Bean的一個屬性。無論是Bean屬性的名稱,,還是賦予該屬性的值,,都在當(dāng)前XML元素中以屬性的形式指定,例如:<article key="page" value="10" />,。
管理父/子關(guān)系
SetNextRule:彈出棧頂?shù)膶ο?,把它傳遞給緊接其下的另一個對象的指定名稱的方法。通常用來把一個已經(jīng)初始化的Bean插入到父對象,。
SetTopRule:把棧里面上數(shù)第二的對象傳遞給頂層的對象,。當(dāng)子對象提供了一個setParenet方法時,這一規(guī)則很有用,。
SetRootRule:調(diào)用棧底對象的一個方法,,并把棧頂?shù)膶ο笞鳛閰?shù)傳入。
調(diào)用任意方法
CallMethodRule:調(diào)用頂層Bean的指定名稱的方法,。被調(diào)用的方法可以有任意多個參數(shù),,參數(shù)的值通過后繼的CallParamRule給出,。
CallParamRule:表示方法調(diào)用的參數(shù),。參數(shù)的值或者取自指定名稱的XML元素的屬性,或者是當(dāng)前元素包含的原始字符數(shù)據(jù),。這個規(guī)則要求用一個整數(shù)指定它在參數(shù)列表中的位置,。
通過XML指定規(guī)則
在前面的內(nèi)容中,我們用程序代碼的方式指定模式和規(guī)則,這些模式和規(guī)則都是在編譯的時候就已經(jīng)確定,,雖然從概念上來講比較簡單,,但卻不能說盡善盡美:Digester框架的總體目標(biāo)是在運行時識別和處理各種數(shù)據(jù)結(jié)構(gòu),但如果我們用編程的方法指定模式和規(guī)則,,則所有行為在編譯時已經(jīng)固定,!如果Java源程序中包含了大量固定的字符串,通常意味著程序在執(zhí)行某些配置操作,,這部分操作可以被(或許是應(yīng)該被)延遲到運行時進行,。
org.apache.commons.digester.xmlrules包解決了這個問題。這個包提供了一個DigesterLoader類,,它能夠從XML文檔讀取模式/規(guī)則對,,返回配置好的Digester對象。用來配置Digester對象的XML文檔必須遵從digester-rules.dtd,,這個DTD是xmlrules包的一部分,。
下面就是本文例子的配置文件rules.xml。有幾點必須說明,。
首先,,模式可以用兩種方式指定:或者使用<pattern>元素,或者通過代表規(guī)則的XML元素的屬性,。這兩種辦法可以混合使用,,且<pattern>元素是可以嵌套的。其次,,<alias>元素和<set-properties-rule>一起使用,,用來把XML屬性映射到Bean屬性。最后,,就當(dāng)前發(fā)行的Digester軟件包而言,,我們不能在配置文件中指定BeanPropertySetterRule,正如前面所介紹的,,我們用CallMethodRule來達到同樣的目標(biāo),。
<?xml version="1.0"?>
<digester-rules>
<object-create-rule pattern="catalog" classname="Catalog" />
<set-properties-rule pattern="catalog" >
<alias attr-name="library" prop-name="library" />
</set-properties-rule>
<pattern value="catalog/book">
<object-create-rule classname="Book" />
<call-method-rule pattern="author" methodname="setAuthor"
paramcount="0" />
<call-method-rule pattern="title" methodname="setTitle"
paramcount="0" />
<set-next-rule methodname="addBook" />
</pattern>
<pattern value="catalog/magazine">
<object-create-rule classname="Magazine" />
<call-method-rule pattern="name" methodname="setName" paramcount="0" />
<pattern value="article">
<object-create-rule classname="Article" />
<set-properties-rule>
<alias attr-name="page" prop-name="page" />
</set-properties-rule>
<call-method-rule pattern="headline" methodname="setHeadline"
paramcount="0" />
<set-next-rule methodname="addArticle" />
</pattern>
<set-next-rule methodname="addMagazine" />
</pattern>
</digester-rules>
|
現(xiàn)在,所有實際的操作都轉(zhuǎn)移到了Digester和DigesterLoader類,,XmlRulesDriver類就變得相當(dāng)簡單,。運行下面的XmlRulesDriver時,在第一個命令行參數(shù)中指定目錄文檔的名字,,在第二個參數(shù)中指定rules.xml(注意,,DigesterLoader不是從File或者org.xml.sax.InputSource讀取rules.xml文件,而是要求指定一個URL,,因此,,下面代碼中File引用被轉(zhuǎn)換成了等價的URL),。
import org.apache.commons.digester.*;
import org.apache.commons.digester.xmlrules.*;
import java.io.*;
import java.util.*;
public class XmlRulesDriver {
public static void main( String[] args ) {
try {
File input = new File( args[0] );
File rules = new File( args[1] );
Digester digester = DigesterLoader.createDigester( rules.toURL() );
Catalog catalog = (Catalog)digester.parse( input );
System.out.println( catalog.toString() );
} catch( Exception exc ) {
exc.printStackTrace();
}
}
}
|
結(jié)束語:本文對Jakarta Commons Digester的介紹就到這里結(jié)束。當(dāng)然,,還有許多內(nèi)容這里尚未涉及,。其中一個在這里忽略的主題是XML名稱空間:Digester允許把規(guī)則定義成只能對某一個名稱空間內(nèi)定義的元素起作用。
另外,,我們簡單地提及了通過擴展Rule類開發(fā)定制規(guī)則的問題,。按照習(xí)慣,Digester類提供了push(),、peek()和pop()方法,,使得開發(fā)者能夠自由地直接操作解析棧。
參考:
Jakarta Commons Digester Homepage
Jakarta Struts Homepage
|