digester使用

1.   概述

解析XML一直都是編寫程序的頭疼問題,不是因爲它難,而是因爲各種地方需要對解析XML的結果要求不同,XML解析和業務邏輯融合在一起,所以每次解析時都感覺是從頭開始,沒有一套好用的類庫。

 

在大多數應用系統中,都需要保存一個或多個配置信息,這些信息可以保存在數據庫中,也可以保存在文件中,保存XML文件中是一個不錯的選擇,如下:

 

<?xml version="1.0" encoding="GB18030"?>

<SystemConfig>

    <LinesPerPage>20</LinesPerPage>

    <ThreadPool>

        <MaxNum>10</MaxNum>

        <MinNum>5</MinNum>

    </ThreadPool>

</SystemConfig>

 

您可能希望在系統運行時,直接從一個JavaBean中讀取系統配置信息,JavaBean的結構如下(省略了get/set):

 

package bean;

 

public class SystemConfig

{

    private int linesPerPage = 0;

 

    private int threadPoolMaxNum = 0;

 

    private int threadPoolMinNum = 0;

}

 

如果簡單的將SystemConfig.xmlSystemConfig關聯起來呢?commons-digester是一個不錯的選擇。

 

2.   Commons-digester介紹

2.1. 概述

Commons-digesterapache的開源類庫,最初是struts的一部分,目的是讀取struts中的一系列XML文件(如struts-config.xml),後來經過擴充和重構,變成了一個獨立的開源庫。

大家都知道,使用struts作爲表示層的應用程序往往都包含多個XML文件,每個XML文件的內容都不同,如果爲每種文件編寫一個XML解析器,將大大加重程序維護的難度,最終可能因爲XML解析方式無法維護,導致這個開源項目失敗。但struts的開發者很聰明,他們想到了將XML解析抽取出來,統一維護的方式,於是commons-disgester的前身就誕生了。

Commons-digester可以通過幾行簡單的代碼,將XML文件轉換爲JavaBean或其他需要的格式。其中,直接轉換爲JavaBean是最常用的。

2.2. 基本原理

Commons-digester將一個XML文件看作一個抽象的棧,當讀到一個開始標籤時,將標籤內容壓棧,當讀取結束標籤時,將棧頂元素出棧,同時執行所有的規則(Rule),以此類推,完成整個XML文件的讀取。

 

3.   SystemConfig.xml解析方法

基本原理可能會讓廣大同行迷惑,直接看一些SystemConfig.xml文件如何解析吧。

解析XML文件的代碼如下,junit部分只是檢驗解析結果是否正確:

 

import java.io.File;

import java.io.IOException;

 

import junit.framework.TestCase;

 

import org.apache.commons.digester.Digester;

import org.xml.sax.SAXException;

 

import bean.SystemConfig;

 

 

public class SystemConfigTest extends TestCase

{

    public void testSystemConfig() throws IOException, SAXException

    {

       // 生成digester對象

       Digester digester = new Digester();

      

       // 當遇到<SystemConfig>標籤時生成SystemConfig對象

       digester.addObjectCreate("SystemConfig", SystemConfig.class);

      

       // 當遇到<SystemConfig><LinesPerPage>標籤時爲SystemConfig中的linesPerPage屬性賦值

       digester.addBeanPropertySetter("SystemConfig/LinesPerPage","linesPerPage");

      

       // 當遇到<SystemConfig><ThreadPool><MaxNum>標籤時爲SystemConfig中的threadPoolMaxNum屬性賦值    digester.addBeanPropertySetter("SystemConfig/ThreadPool/MaxNum","threadPoolMaxNum");

      

       // 當遇到<SystemConfig><ThreadPool><MinNum>標籤時爲SystemConfig中的threadPoolMinNum屬性賦值    digester.addBeanPropertySetter("SystemConfig/ThreadPool/MinNum","threadPoolMinNum");

      

       // 讀取SystemConfig.xml文件

       File systemConfigXml = new File("SystemConfig.xml");

       // 進行文件解析

       SystemConfig sysConfig = (SystemConfig)digester.parse(systemConfigXml);

      

      

       // 以下的測試用例用來判斷解析結果是否正確

       assertNotNull(sysConfig);

       assertEquals(20, sysConfig.getLinesPerPage());

       assertEquals(10, sysConfig.getThreadPoolMaxNum());

       assertEquals(5, sysConfig.getThreadPoolMinNum());

    }

}

 

要解析一個XML文件,需要先定義好解析規則,然後使用這些預定義規則解析XML文件即可。在上面的例子中,因爲解析的結果是將XML中的屬性值保存到Bean中,所以直接使用addBeanPropertySetter()方法就可以了。

從上面的例子可以看出,解析一個XML文件,主要有以下三步:

1.     創建Digester對象。

2.     Digester對象中添加解析規則。

3.     使用解析規則解析文件。

4.   XML文件解析規則

Commons-digester將繁瑣的解析過程變成了簡單的三個步驟,其實解析不同XML文件時,只有添加規則一步有一些差別,其他兩部只要照搬上面的代碼即可。

規則是XML文件的解析方法,也就是Digester對象在遇到不同XML元素時需要執行的操作。比如上面例子中的BeanPropertySetter規則,就是將XML內容保存到Bean的相應元素中。

規則可以自定義,但在一般情況下,默認的一套規則足以滿足大多數場合的需要。

5.   更復雜的例子

下面我們來看一個更復雜的例子,在這個例子中,使用commons-digester解析一個web.xml文件,這個文件是我自己寫的一個真實項目中的一小部分。

Web.xml文件如下:

 

<?xml version="1.0" encoding="GB18030"?>

 

<web-app>

    <display-name>Struts Examples Application</display-name>

    <context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>/WEB-INF/spring-beans-config.xml</param-value>

    </context-param>

 

    <listener>

       <listener-class>

           org.springframework.web.context.ContextLoaderListener

       </listener-class>

    </listener>

 

    <listener>

       <listener-class>

           com.lijin.demo.common.listener.SpringConfigListener

       </listener-class>

    </listener>

 

    <filter>

       <filter-name>encodingFilter</filter-name>

       <filter-class>

           org.springframework.web.filter.CharacterEncodingFilter

       </filter-class>

       <init-param>

           <param-name>encoding</param-name>

           <param-value>UTF-8</param-value>

       </init-param>

       <init-param>

           <param-name>forceEncoding</param-name>

           <param-value>true</param-value>

       </init-param>

    </filter>

   

    <servlet>

       <servlet-name>action</servlet-name>

       <servlet-class>

           org.apache.struts.action.ActionServlet

       </servlet-class>

 

       <init-param>

           <param-name>config</param-name>

           <param-value>/WEB-INF/struts-configs/common-config.xml</param-value>

       </init-param>

       <init-param>

           <param-name>config/jsp/user</param-name>

           <param-value>/WEB-INF/struts-configs/user-config.xml</param-value>

       </init-param>

       <init-param>

           <param-name>config/jsp/usergroup</param-name>

           <param-value>/WEB-INF/struts-configs/usergroup-config.xml</param-value>

       </init-param>

       <load-on-startup>2</load-on-startup>

    </servlet>

 

    <servlet-mapping>

       <servlet-name>action</servlet-name>

       <url-pattern>*.do</url-pattern>

    </servlet-mapping>

 

    <filter-mapping>

       <filter-name>encodingFilter</filter-name>

       <url-pattern>*.html</url-pattern>

    </filter-mapping>

   

    <filter-mapping>

       <filter-name>encodingFilter</filter-name>

       <url-pattern>*.do</url-pattern>

    </filter-mapping>

   

    <filter-mapping>

       <filter-name>encodingFilter</filter-name>

       <url-pattern>*.jsp</url-pattern>

    </filter-mapping>

   

    <welcome-file-list>

       <welcome-file>index.jsp</welcome-file>

    </welcome-file-list>

 

    <taglib>

       <taglib-uri>http://www.lijin.com/tags</taglib-uri>

       <taglib-location>/WEB-INF/tld/lijin.tld</taglib-location>

    </taglib>

</web-app>

 

解析該文件的例子如下:

 

import java.io.File;

import java.io.IOException;

 

import junit.framework.TestCase;

 

import org.apache.commons.digester.Digester;

import org.xml.sax.SAXException;

 

import bean.Filter;

import bean.Servlet;

import bean.WebApp;

 

 

public class FirstTest extends TestCase

{

    public void testWebXml() throws IOException, SAXException

    {

       Digester digester = new Digester();

       digester.addObjectCreate("web-app", WebApp.class);

       digester.addBeanPropertySetter("web-app/display-name""displayName");

      

       digester.addCallMethod("web-app/context-param""addContextParam", 2);

       digester.addCallParam("web-app/context-param/param-name", 0);

       digester.addCallParam("web-app/context-param/param-value", 1);

      

       digester.addCallMethod("web-app/listener""addListener", 1);

       digester.addCallParam("web-app/listener/listener-class", 0);

      

       digester.addObjectCreate("web-app/filter", Filter.class);

       digester.addSetNext("web-app/filter""addFilter");

       digester.addBeanPropertySetter("web-app/filter/filter-name""filterName");

       digester.addBeanPropertySetter("web-app/filter/filter-class""filterClass");

       digester.addCallMethod("web-app/filter/init-param""addInitParam", 2);

       digester.addCallParam("web-app/filter/init-param/param-name", 0);

       digester.addCallParam("web-app/filter/init-param/param-value", 1);

      

       digester.addObjectCreate("web-app/servlet", Servlet.class);

       digester.addSetNext("web-app/servlet""addServlet");

       digester.addBeanPropertySetter("web-app/servlet/servlet-name""servletName");

       digester.addBeanPropertySetter("web-app/servlet/servlet-class""servletClass");

       digester.addCallMethod("web-app/servlet/init-param""addInitParam", 2);

       digester.addCallParam("web-app/servlet/init-param/param-name", 0);

       digester.addCallParam("web-app/servlet/init-param/param-value", 1);

       digester.addBeanPropertySetter("web-app/servlet/load-on-startup""loadOnStartUp");

      

       digester.addCallMethod("web-app/servlet-mapping""addServletMapping", 2);

       digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);

       digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);

      

       digester.addCallMethod("web-app/filter-mapping""addFilterMapping", 2);

       digester.addCallParam("web-app/filter-mapping/filter-name", 0);

       digester.addCallParam("web-app/filter-mapping/url-pattern", 1);

      

       digester.addCallMethod("web-app/welcome-file-list/welcome-file""addWelcomeFile", 1);

       digester.addCallParam("web-app/welcome-file-list/welcome-file", 0);

      

       digester.addCallMethod("web-app/taglib""addTaglib", 2);

       digester.addCallParam("web-app/taglib/taglib-uri", 0);

       digester.addCallParam("web-app/taglib/taglib-location", 1);

      

       File webXml = new File("web.xml");

       WebApp webApp = (WebApp)digester.parse(webXml);

      

       assertNotNull(webApp);

       assertEquals("Struts Examples Application", webApp.getDisplayName());

      

       assertEquals(1, webApp.getContextParamList().size());

       assertEquals("contextConfigLocation", webApp.getContextParamList().get(0).getParamName());

       assertEquals("/WEB-INF/spring-beans-config.xml", webApp.getContextParamList().get(0).getParamValue());

      

       assertEquals(2, webApp.getListenerList().size());

       assertEquals("org.springframework.web.context.ContextLoaderListener", webApp.getListenerList().get(0));

       assertEquals("com.lijin.demo.common.listener.SpringConfigListener", webApp.getListenerList().get(1));

      

       assertEquals(1, webApp.getFilterMap().size());

       assertEquals("encodingFilter", webApp.getFilterMap().get("encodingFilter").getFilterName());

       assertEquals("org.springframework.web.filter.CharacterEncodingFilter", webApp.getFilterMap().get("encodingFilter").getFilterClass());

       assertEquals("UTF-8", webApp.getFilterMap().get("encodingFilter").getEncoding());

       assertEquals(true, webApp.getFilterMap().get("encodingFilter").isForceEncoding());

      

       assertEquals(1, webApp.getServletMap().size());

       assertTrue(webApp.getServletMap().containsKey("action"));

       assertEquals("action", webApp.getServletMap().get("action").getServletName());

       assertEquals("org.apache.struts.action.ActionServlet", webApp.getServletMap().get("action").getServletClass());

       assertEquals(3, webApp.getServletMap().get("action").getConfigMap().size());

       assertTrue(webApp.getServletMap().get("action").getConfigMap().containsKey("config"));

      assertTrue(webApp.getServletMap().get("action").getConfigMap().containsKey("config/jsp/user"));

       assertTrue(webApp.getServletMap().get("action").getConfigMap().containsKey("config/jsp/usergroup"));

       assertEquals("/WEB-INF/struts-configs/common-config.xml", webApp.getServletMap().get("action").getConfigMap().get("config"));

       assertEquals("/WEB-INF/struts-configs/user-config.xml", webApp.getServletMap().get("action").getConfigMap().get("config/jsp/user"));

       assertEquals("/WEB-INF/struts-configs/usergroup-config.xml", webApp.getServletMap().get("action").getConfigMap().get("config/jsp/usergroup"));

       assertEquals(webApp.getServletMap().get("action").getLoadOnStartUp(), 2);

      

       assertEquals(1, webApp.getServletMap().get("action").getMappingUrlList().size());

       assertTrue(webApp.getServletMap().get("action").getMappingUrlList().contains("*.do"));

      

       assertEquals(1, webApp.getFilterMap().size());

       assertEquals(3, webApp.getFilterMap().get("encodingFilter").getMappingUrlList().size());

      assertTrue(webApp.getFilterMap().get("encodingFilter").getMappingUrlList().contains("*.html"));

       assertTrue(webApp.getFilterMap().get("encodingFilter").getMappingUrlList().contains("*.do"));

      assertTrue(webApp.getFilterMap().get("encodingFilter").getMappingUrlList().contains("*.jsp"));

      

       assertEquals(1, webApp.getWelcomeFileList().size());

       assertTrue(webApp.getWelcomeFileList().contains("index.jsp"));

      

       assertEquals(1, webApp.getTagLibMap().size());

       assertTrue(webApp.getTagLibMap().containsKey("http://www.lijin.com/tags"));

       assertEquals("/WEB-INF/tld/lijin.tld", webApp.getTagLibMap().get("http://www.lijin.com/tags"));

    }

   

   

}

 

 

其中用到了bean.Filterbean.Servletbean.WebApp三個JavaBean,他們都是配合這個例子的標準JavaBean,都可以根據上述文件的結構推倒出來,在這裏不贅述。

6.   學習這個類庫的心得

Commons-Digester爲我們解析XML文件提供了一個新思路和新方法,在研究的規程中,我曾經想過爲什麼只有讀取文件的類,沒有寫文件的類,後來仔細一想,才明白設計這個類庫的目的是簡化XML文件的讀取,相反,寫XML文件比讀取容易的多,因爲整個寫文件的過程都在自己的控制之下,能輸出什麼格式自己心裏都有數,讀取就沒那麼容易了需要進行各種各樣的校驗。實際使用時,在對這套類庫進行簡單的封裝後,就可以實現讀寫XML配置文件的功能了。

發佈了102 篇原創文章 · 獲贊 27 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章