天天看點

Digester解析xml檔案

剛剛學了一下Digester如何解析xml檔案,是以記錄下來,友善以後檢視。

    一般用來讀取xml檔案的工具包有DOM、SAX和JDOM等,但用過的人都知道,它們屬于比較底層的API,寫起來代碼量很大,而且如果修改了xml檔案的格式,代碼也要做大幅度的改動。而使用Apache Jakarta的Digester,解析XML檔案非常友善且不需要過多的關心底層的具體解析過程。Digester本來僅僅是Jakarta Struts中的一個工具,用于處理struts-config.xml配置檔案。顯然,将XML檔案轉換成相應的Java對象是一項很通用的功能,這個工具理應具有更廣泛的用途,是以很快它就在Jakarta Commons項目(用于提供可重用的Java元件庫)中有了一席之地。Digester由"事件"驅動,通過調用預定義的規則操作對象棧,将XML檔案轉換為Java對象。

    工作原理如下: Digester底層采用SAX(Simple API for XML)析XML檔案,是以很自然的,對象轉換由"事件"驅動,在周遊每個節點時,檢查是否有比對模式,如果有,則執行規則定義的操作,比如建立特定的Java對象,或調用特定對象的方法等。此處的XML元素根據比對模式(matching pattern)識别,而相關操作由規則(rule)定義。

    如下xml代碼,右邊是左邊元素對應的比對模式:

[xhtml] view plain copy

  1. <datasources>          'datasources'   
  2.   <datasource>         'datasources/datasource'   
  3.     <name/>            'datasources/datasource/name'   
  4.     <driver/>          'datasources/datasource/driver'    
  5.   </datasource>   
  6.   <datasource>         'datasources/datasource'   
  7.     <name/>            'datasources/datasource/name'   
  8.     <driver/>          'datasources/datasource/driver'    
  9.   </datasource>   
  10. </datasources>   

例子1:

下面介紹解析xml檔案的代碼

下面是存放位址及編碼的xml檔案viewcache.xml(片段):

[xhtml] view plain copy

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <viewcache>  
  3.     <areas>  
  4.         <area>  
  5.             <id>1098</id>  
  6.             <parentId>1001</parentId>  
  7.             <areaType>province</areaType>  
  8.             <name>北京</name>  
  9.             <ordering>1867</ordering>  
  10.         </area>  
  11.         <area>  
  12.             <id>1099</id>  
  13.             <parentId>1098</parentId>  
  14.             <areaType>capital</areaType>  
  15.             <name>北京</name>  
  16.             <ordering>1868</ordering>  
  17.             <phoneArea>010</phoneArea>  
  18.         </area>  
  19.         <area>  
  20.             <id>4476</id>  
  21.             <parentId>1099</parentId>  
  22.             <areaType>county</areaType>   
  23.             <name>北京市朝陽區</name>  
  24.             <ordering>1869</ordering>  
  25.             <phoneArea>010</phoneArea>  
  26.         </area>  
  27.         <area>  
  28.             <id>4477</id>  
  29.             <parentId>1099</parentId>  
  30.             <areaType>county</areaType>  
  31.             <name>北京市崇文區</name>  
  32.             <ordering>1870</ordering>  
  33.             <phoneArea>010</phoneArea>  
  34.         </area>  
  35.         <area>  
  36.             <id>4478</id>  
  37.             <parentId>1099</parentId>  
  38.             <areaType>county</areaType>  
  39.             <name>北京市大興區</name>  
  40.             <ordering>1871</ordering>  
  41.             <phoneArea>010</phoneArea>  
  42.         </area>  
  43.     </areas>  
  44. </viewcache>  

此xml檔案分3層結構,分别為:

<viewcache>節點 其下包含1個<areas>節點

<areas>節點 其下包含多個<area>節點

<area>節點,其下包含各種資訊節點 : 如:<id> 、<name>等。 

我們的操作目标是把area中的資訊節點的内容提取出來。 

把每個<arrea>看做為一個對象,<area>中資訊節點的内容為對象中的元素。 

設定一個類Area.java 其内容如下: 

[java] view plain copy

  1. public class Area {  
  2.     private int    id;  
  3.     private String name;  
  4.     private String areaType;  
  5.     private int    parentId;  
  6.     private int    ordering;  
  7.     private String zip;  
  8.     private String phoneArea;  
  9.     public int getOrdering() {  
  10.         return ordering;  
  11.     }  
  12.     public void setOrdering(int ordering) {  
  13.         this.ordering = ordering;  
  14.     }  
  15.     public String getAreaType() {  
  16.         return areaType;  
  17.     }  
  18.     public void setAreaType(String areaType) {  
  19.         this.areaType = areaType;  
  20.     }  
  21.     public int getId() {  
  22.         return id;  
  23.     }  
  24.     public void setId(int id) {  
  25.         this.id = id;  
  26.     }  
  27.     public String getName() {  
  28.         return name;  
  29.     }  
  30.     public void setName(String name) {  
  31.         this.name = name;  
  32.     }  
  33.     public int getParentId() {  
  34.         return parentId;  
  35.     }  
  36.     public void setParentId(int parentId) {  
  37.         this.parentId = parentId;  
  38.     }  
  39.     public String getZip() {  
  40.         return zip;  
  41.     }  
  42.     public void setZip(String zip) {  
  43.         this.zip = zip;  
  44.     }  
  45.     public String getPhoneArea() {  
  46.         return phoneArea;  
  47.     }  
  48.     public void setPhoneArea(String phoneArea) {  
  49.         this.phoneArea = phoneArea;  
  50.     }  
  51. }  

建立一個ViewCache類,用來儲存解析後的所有對象:

[java] view plain copy

  1. public class ViewCache {  
  2.     private List areaList             = new ArrayList();  
  3.     public List getAreaList() {  
  4.         return areaList;  
  5.     }  
  6.     public void setAreaList(List areaList) {  
  7.         this.areaList = areaList;  
  8.     }  
  9.     // 供Digester調用的方法  
  10.     public void addArea(Area area) {  
  11.         this.areaList.add(area);  
  12.     }  
  13. }  

建立一個類AreaDigester,對xml檔案進行解析:

[java] view plain copy

public class AreaDigester {  
          
        public ViewCache digester() throws Exception {  
            Digester digester = new Digester();  
            digester.setValidating(false);  
            digester.addObjectCreate("viewcache/areas", ViewCache.class);  
            // 指明比對模式和要建立的類   
            digester.addObjectCreate("viewcache/areas/area", Area.class);  
            // 設定對象屬性,與xml檔案對應,不設定則是預設  
            digester.addBeanPropertySetter("viewcache/areas/area/id", "id");  
            digester.addBeanPropertySetter("viewcache/areas/area/parentId", "parentId");  
            digester.addBeanPropertySetter("viewcache/areas/area/name", "name");  
            digester.addBeanPropertySetter("viewcache/areas/area/areaType", "areaType");  
            digester.addBeanPropertySetter("viewcache/areas/area/ordering", "ordering");  
            digester.addBeanPropertySetter("viewcache/areas/area/zip", "zip");  
            digester.addBeanPropertySetter("viewcache/areas/area/phoneArea", "phoneArea");  
            // 當移動到下一個标簽中時的動作  
            digester.addSetNext("viewcache/areas/area", "addArea");  
              
            ViewCache vc = null;  
            try {  

                File file = new File("viewcache.xml");
                BufferedReader read = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
                StringBuffer sb = new StringBuffer();
                String content = "";
                while((content=read.readLine()) != null){
                      sb.append(content);
                }
                vc = (ViewCache) digester.parse(new StringReader(sb.toString()));  
            } catch (IOException e) {  
                throw new Exception(e);  
            } catch (SAXException e) {  
                throw new Exception(e);  
            }  
            return vc;  
        }  

    }  



           

調用AreaDigester的digester方法,即可把解析後的所有位址對象,存放在ViewCache的list中。

例子2:

要解析的xml檔案books.xml如下:

[xhtml] view plain copy

  1. <?xml version="1.0" encoding="UTF-8" ?>   
  2. <library name="alibaba圖書館">   
  3.      <book title ="thinking in java"  author="xxx">   
  4.          <chapter>   
  5.              <no>第一章</no>   
  6.              <caption>第一章的标題</caption>   
  7.          </chapter>   
  8.          <chapter>   
  9.              <no>第二章</no>   
  10.              <caption>第二章的标題</caption>   
  11.          </chapter>   
  12.      </book>   
  13.      <book title="effective java"  author="yyy">   
  14.          <chapter>   
  15.              <no>第一章</no>   
  16.              <caption>第一章的标題</caption>   
  17.          </chapter>   
  18.      </book>   
  19. </library>  

Library類如下:

[java] view plain copy

  1. public class Library {  
  2.     private String name;  
  3.     private List<Book> bookList = new ArrayList<Book>();  
  4.     public String getName() {  
  5.         return name;  
  6.     }  
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10.     public List<Book> getBookList() {  
  11.         return bookList;  
  12.     }  
  13.     public void addBook(Book book) {  
  14.         bookList.add(book);  
  15.     }  
  16. }  

Book類如下:

[java] view plain copy

  1. public class Book {  
  2.     private String        title;  
  3.     private String        author;  
  4.     private List<Chapter> chapters = new ArrayList<Chapter>();  
  5.     public void setBookInfo(String title, String author) {  
  6.         this.title = title;  
  7.         this.author = author;  
  8.     }  
  9.     public void addChapter(Chapter chapter) {  
  10.         this.chapters.add(chapter);  
  11.     }  
  12.     public String getTitle() {  
  13.         return title;  
  14.     }  
  15.     public void setTitle(String title) {  
  16.         this.title = title;  
  17.     }  
  18.     public String getAuthor() {  
  19.         return author;  
  20.     }  
  21.     public void setAuthor(String author) {  
  22.         this.author = author;  
  23.     }  
  24.     public List<Chapter> getChapters() {  
  25.         return chapters;  
  26.     }  
  27.     public void setChapters(List<Chapter> chapters) {  
  28.         this.chapters = chapters;  
  29.     }  
  30. }  

Chapter類如下:

[java] view plain copy

  1. public class Chapter {  
  2.     private String no;  
  3.     private String caption;  
  4.     public String getNo() {  
  5.         return no;  
  6.     }  
  7.     public void setNo(String no) {  
  8.         this.no = no;  
  9.     }  
  10.     public String getCaption() {  
  11.         return caption;  
  12.     }  
  13.     public void setCaption(String caption) {  
  14.         this.caption = caption;  
  15.     }  
  16. }  

解析xml的類如下:

[java] view plain copy

  1. public class MainTest {  
  2.     public static void main(String[] args) {  
  3.         // 建立一個Digester對象  
  4.         Digester digester = new Digester();  
  5.         //指定它不要用DTD驗證XML文檔的合法性——這是因為我們沒有為XML文檔定義DTD  
  6.         digester.setValidating(false);  
  7.         // 從library标簽開始解析,并建立一個Library對象做為根對象  
  8.         digester.addObjectCreate("library", Library.class);  
  9.         // 根據library标簽屬性值設定對象的屬性,一次可以設定多個屬性  
  10.         digester.addSetProperties("library");  
  11.         // 也可以用下面的方法,指定propertyName  
  12.         // digester.addSetProperties("library", "name", "name");  
  13.         // -----第1層元素開始  
  14.         digester.addObjectCreate("library/book", Book.class);  
  15.         //digester.addSetProperties("library/book");  
  16.         // 可以用以下三條語句代替  
  17.         digester.addCallMethod("library/book", "setBookInfo", 2);  
  18.         digester.addCallParam("library/book", 0, "title");  
  19.         digester.addCallParam("library/book", 1, "author");  
  20.         // -----第2層元素開始  
  21.         digester.addObjectCreate("library/book/chapter", Chapter.class);  
  22.         digester.addBeanPropertySetter("library/book/chapter/no");  
  23.         // digester.addBeanPropertySetter("library/book/chapter/no", "no");  
  24.         // digester.addBeanPropertySetter("library/book/chapter/caption");  
  25.         // 下面的方法,可以用來代替上一句,作用是一樣的   
  26.         digester.addCallMethod("library/book/chapter/caption", "setCaption", 0);  
  27.         // addSetNext()是說在再次遇到比對節點後, 調用目前對象(Chapter類的對象)的父對象(Book類的對象)的方法,方法參數是目前層元素的對象  
  28.         digester.addSetNext("library/book/chapter", "addChapter");  
  29.         // -----第2層元素結束  
  30.         digester.addSetNext("library/book", "addBook");  
  31.         // -----第1層元素結束  
  32.         try {  
  33.             // 解析XML檔案,并得到ROOT元素  
  34.             Library library = (Library) digester.parse(MainTest.class.getResourceAsStream("books.xml"));  
  35.             System.out.println(" 圖書館: " + library.getName());  
  36.             System.out.println(" 共藏書: " + library.getBookList().size() + " 本 ");  
  37.             System.out.println(" ***************************** ");  
  38.             for (Book book : library.getBookList()) {  
  39.                 System.out.println(" 書名: " + book.getTitle() + "        作者: " + book.getAuthor());  
  40.                 System.out.println(" ------------------------------ ");  
  41.                 // 顯示章節  
  42.                 System.out.println(" 共 " + book.getChapters().size() + " 章 ");  
  43.                 for (Chapter chapter : book.getChapters()) {  
  44.                     System.out.println(chapter.getNo() + ": " + chapter.getCaption());  
  45.                 }  
  46.                 System.out.println(" ------------------------------ ");  
  47.             }  
  48.         } catch (IOException e) {  
  49.             e.printStackTrace();  
  50.         } catch (SAXException e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.     }  
  54. }  

例子3:

Digester解析xml的規則,除了在java類中描述設定之外,還可以把解析規則放在xml檔案中。以例子2中的代碼為例,規則在books-rule.xml檔案中,内容如下:(The DTD is distributed in the

commons-digester.jar

. It can be found at

org/apache/commons/digester/xmlrules/digester-rules.dtd,通過檢視DTD檔案,可以知道有哪些标簽可以使用

)

[xhtml] view plain copy

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE digester-rules PUBLIC  
  3.    "-//Jakarta Apache //DTD digester-rules XML V1.0//EN"  
  4.    "digester-rules.dtd">   
  5. <digester-rules>    
  6.     <object-create-rule pattern="library" classname="com.alibaba.chj.digester.Library" />    
  7.     <set-properties-rule pattern="library">    
  8.         <alias attr-name="name" prop-name="name" />    
  9.     </set-properties-rule>    
  10.     <pattern value="library/book">    
  11.         <object-create-rule classname="com.alibaba.chj.digester.Book" />    
  12.         <set-properties-rule />    
  13.         <pattern value="chapter">    
  14.             <object-create-rule classname="com.alibaba.chj.digester.Chapter" />    
  15.             <bean-property-setter-rule pattern="no" propertyname="no" />  
  16.             <bean-property-setter-rule pattern="caption" propertyname="caption" />  
  17.             <set-next-rule methodname="addChapter" />    
  18.         </pattern>       
  19.         <set-next-rule methodname="addBook" />    
  20.     </pattern>    
  21. </digester-rules>    

解析xml類的代碼,修改為:

[java] view plain copy

  1. public class MainTest {  
  2.     public static void main(String[] args) {  
  3.         try {      
  4.             Digester digester = DigesterLoader.createDigester(DigesterXmlRuleTest.class.getResource("books-rule.xml"));      
  5.             Library library = (Library) digester.parse(DigesterXmlRuleTest.class.getResourceAsStream("books.xml"));      
  6.             System.out.println(" 圖書館: " + library.getName());  
  7.             System.out.println(" 共藏書: " + library.getBookList().size() + " 本 ");  
  8.             System.out.println(" ***************************** ");  
  9.             for (Book book : library.getBookList()) {  
  10.                 System.out.println(" 書名: " + book.getTitle() + "        作者: " + book.getAuthor());  
  11.                 System.out.println(" ------------------------------ ");  
  12.                 // 顯示章節  
  13.                 System.out.println(" 共 " + book.getChapters().size() + " 章 ");  
  14.                 for (Chapter chapter : book.getChapters()) {  
  15.                     System.out.println(chapter.getNo() + ": " + chapter.getCaption());  
  16.                 }  
  17.                 System.out.println(" ------------------------------ ");  
  18.             }     
  19.         } catch (IOException e) {  
  20.             e.printStackTrace();  
  21.         } catch (SAXException e) {  
  22.             e.printStackTrace();  
  23.         }   
  24.     }  
  25. }  

用于規則放在xml檔案中,是以解析的類,顯得更加簡潔一些。