在一個比較完整的應用系統裡,經常需要有一些設定檔。簡單的屬性使用.properties檔案即可,但要配置一些複雜物件,則應該考慮使用xml檔案。一般用來讀取xml檔案的工具包有DOM、SAX和JDOM等,但用過的人都知道,它們屬於比較底層的API,寫起來代碼量很大,而且如果修改了xml檔案的格式,代碼也要做大幅度的改動。Jakarta Commons項目裡的Digester包,可以輕鬆實現xml檔案到Java對象的轉換,請看下面這個例子。
在一個項目裡,需要提供一些統計圖,但圖的內容暫時未能確定。所以我決定讓圖可以配置,所有定義儲存在一個名為charts.xml(或國際化後的檔案名稱如charts_zh_CN.xml,這裡只考慮預設語言)的檔案內,下面是該檔案的部分內容:
<?xml version="1.0" encoding="UTF-8" ?><charts> <chart id="BarChart1" > <title>統計圖一</title> <legendVisible>false</legendVisible> <toolTipsVisible>true</toolTipsVisible> <type>Bar</type> <labelx>時間</labelx> <labely>資料</labely> <width>500</width> <height>360</height> <hql>select count(c),c.department.name from edu.pku.pub.aims.model.business.Client c group by c.department</hql> <description></description> </chart></charts>
可以看出,我為每個圖定義了id、title、legendVisible等等屬性,這些屬性的意義都很明顯,它們將影響統計圖的資料和在頁面中的表現。在程式裡,我需要把這個檔案裡的定義讀到一個註冊表類ChartRegistry裡,該註冊表維護一個java.util.List類型的registry變數,其中每個元素是一個ChartConfig類。現在Digester該顯示它的價值了。
為了方便使用Digester,我們讓ChartConfig也具有統計圖的每個屬性(id、title、legendVisible等等),名稱與charts.xml裡的元素的屬性(子項目)一一對應,並且都具有getter和setter方法,也就是說,ChartConfig是一個bean類。在ChartRegistry類裡定義一個deregister()方法,它的作用是用Digester讀入並解析指定的xml檔案,代碼如下;還有一個register()方法用來把ChartConfig對象加到registry裡。
public void deregister(URL url) throws IOException,SAXException{ InputStream is = new FileInputStream(url.getFile()); Digester digester = new Digester(); digester.push(this); digester.setValidating(false); digester.addObjectCreate("charts/chart", ChartConfig.class); digester.addSetProperties("charts/chart"); digester.addBeanPropertySetter("charts/chart/legendVisible"); digester.addBeanPropertySetter("charts/chart/toolTipsVisible"); digester.addBeanPropertySetter("charts/chart/title"); digester.addBeanPropertySetter("charts/chart/type"); digester.addBeanPropertySetter("charts/chart/labelx"); digester.addBeanPropertySetter("charts/chart/labely"); digester.addBeanPropertySetter("charts/chart/width"); digester.addBeanPropertySetter("charts/chart/height"); digester.addBeanPropertySetter("charts/chart/hql"); digester.addBeanPropertySetter("charts/chart/description"); digester.addSetNext("charts/chart","register"); digester.parse(is); Collections.sort(registry);}
基本上來說,Digester和SAX解析xml的過程很像,它的原理就是制定一些規則,在遍曆每個節點時檢查是否有匹配的規則,如果有就執行對應的操作。例如,上面的代碼中,“digester.addObjectCreate("charts/chart", ChartConfig.class);”這一句的作用是告訴Digester:如果遇到匹配“charts/chart”形式的節點,就執行一個“對象建立”操作,建立什麼對象呢,應該建立Class為“ChartConfig.class”的對象;類似的,addSetProperties()是告訴Digester將指定節點的屬性全部映射到對象的屬性,在這個例子裡指的就是id屬性;addBeanPropertySetter()是將子節點轉換為對象的屬性,這個方法還可以有第二個參數,當對象的屬性名稱和子節點的名字不一樣時用來指定對象的屬性名稱;addSetNext()是說在遇到匹配節點後,對當前對象的父物件執行一個方法,參數是當前參數,對這個例子來說就是執行ChartConfig.register(ChartConfig)方法。因此這樣構造得到的Digester會把charts.xml裡的每個元素轉換為一個ChartConfig對象,並register到ChartRegistry裡。
順利得到了ChartRegister對象,我就可以在程式雷根據它的內容構造統計圖了(統計圖一般使用jfreechart來產生,這裡就不贅述了)。與Digester具有類似功能的工具包其實還有不少,例如Caster、Jato等等,我沒有實際使用過它們,但因為我對用過的Jakarta其他項目都很滿意(例如BeanUtils、HttpClient,品牌效應?),所以一開始就選擇了Digester:簡單方便。