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代碼,右邊是左邊元素對應的匹配模式:



<datasources>          'datasources'   
  <datasource>         'datasources/datasource'   
    <name/>            'datasources/datasource/name'   
    <driver/>          'datasources/datasource/driver'    
  </datasource>   
  <datasource>         'datasources/datasource'   
    <name/>            'datasources/datasource/name'   
    <driver/>          'datasources/datasource/driver'    
  </datasource>   
</datasources>   

例子1:

下面介紹解析xml文件的代碼

下面是存放地址及編碼的xml文件viewcache.xml(片段):

<?xml version="1.0" encoding="UTF-8" ?>  
<viewcache>  
    <areas>  
        <area>  
            <id>1098</id>  
            <parentId>1001</parentId>  
            <areaType>province</areaType>  
            <name>北京</name>  
            <ordering>1867</ordering>  
        </area>  
        <area>  
            <id>1099</id>  
            <parentId>1098</parentId>  
            <areaType>capital</areaType>  
            <name>北京</name>  
            <ordering>1868</ordering>  
            <phoneArea>010</phoneArea>  
        </area>  
        <area>  
            <id>4476</id>  
            <parentId>1099</parentId>  
            <areaType>county</areaType>   
            <name>北京市朝陽區</name>  
            <ordering>1869</ordering>  
            <phoneArea>010</phoneArea>  
        </area>  
        <area>  
            <id>4477</id>  
            <parentId>1099</parentId>  
            <areaType>county</areaType>  
            <name>北京市崇文區</name>  
            <ordering>1870</ordering>  
            <phoneArea>010</phoneArea>  
        </area>  
        <area>  
            <id>4478</id>  
            <parentId>1099</parentId>  
            <areaType>county</areaType>  
            <name>北京市大興區</name>  
            <ordering>1871</ordering>  
            <phoneArea>010</phoneArea>  
        </area>  
    </areas>  
</viewcache>  

此xml文件分3層結構,分別爲:

節點 其下包含1個節點

節點 其下包含多個節點

節點,其下包含各種信息節點 : 如: 、等。

我們的操作目標是把area中的信息節點的內容提取出來。
把每個看做爲一個對象,中信息節點的內容爲對象中的元素。
設定一個類Area.java 其內容如下:

public class Area {  
    private int    id;  
    private String name;  
    private String areaType;  
    private int    parentId;  
    private int    ordering;  
    private String zip;  

    private String phoneArea;  

    public int getOrdering() {  
        return ordering;  
    }  
    public void setOrdering(int ordering) {  
        this.ordering = ordering;  
    }  
    public String getAreaType() {  
        return areaType;  
    }  
    public void setAreaType(String areaType) {  
        this.areaType = areaType;  
    }  
    public int getId() {  
        return id;  
    }  
    public void setId(int id) {  
        this.id = id;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getParentId() {  
        return parentId;  
    }  
    public void setParentId(int parentId) {  
        this.parentId = parentId;  
    }  

    public String getZip() {  
        return zip;  
    }  

    public void setZip(String zip) {  
        this.zip = zip;  
    }  

    public String getPhoneArea() {  
        return phoneArea;  
    }  

    public void setPhoneArea(String phoneArea) {  
        this.phoneArea = phoneArea;  
    }  
}  

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

public class ViewCache {  
    private List areaList             = new ArrayList();  
    public List getAreaList() {  
        return areaList;  
    }  
    public void setAreaList(List areaList) {  
        this.areaList = areaList;  
    }  

    // 供Digester調用的方法  
    public void addArea(Area area) {  
        this.areaList.add(area);  
    }  
}  

創建一個類AreaDigester,對xml文件進行解析:

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 {  
            vc = (ViewCache) digester.parse("viewcache.xml");  
        } catch (IOException e) {  
            throw new Exception(e);  
        } catch (SAXException e) {  
            throw new Exception(e);  
        }  
        return vc;  
    }  
}  

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

例子2:

要解析的xml文件books.xml如下:

<?xml version="1.0" encoding="UTF-8" ?>   
<library name="alibaba圖書館">   
     <book title ="thinking in java"  author="xxx">   
         <chapter>   
             <no>第一章</no>   
             <caption>第一章的標題</caption>   
         </chapter>   
         <chapter>   
             <no>第二章</no>   
             <caption>第二章的標題</caption>   
         </chapter>   
     </book>   
     <book title="effective java"  author="yyy">   
         <chapter>   
             <no>第一章</no>   
             <caption>第一章的標題</caption>   
         </chapter>   
     </book>   
</library>  

Library類如下:

public class Library {  
    private String name;  
    private List<Book> bookList = new ArrayList<Book>();  

    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }  

    public List<Book> getBookList() {  
        return bookList;  
    }  

    public void addBook(Book book) {  
        bookList.add(book);  
    }  
}  

Book類如下:

public class Book {  

    private String        title;  
    private String        author;  
    private List<Chapter> chapters = new ArrayList<Chapter>();  

    /** 
     * 這個方法,用來演示xml的解析時用的另一種方式 
     * @param title 
     * @param author 
     */  
    public void setBookInfo(String title, String author) {  
        this.title = title;  
        this.author = author;  
    }  

    public void addChapter(Chapter chapter) {  
        this.chapters.add(chapter);  
    }  

    public String getTitle() {  
        return title;  
    }  

    public void setTitle(String title) {  
        this.title = title;  
    }  

    public String getAuthor() {  
        return author;  
    }  

    public void setAuthor(String author) {  
        this.author = author;  
    }  

    public List<Chapter> getChapters() {  
        return chapters;  
    }  

    public void setChapters(List<Chapter> chapters) {  
        this.chapters = chapters;  
    }  
}  

Chapter類如下:

public class Chapter {  

    private String no;  
    private String caption;  

    public String getNo() {  
        return no;  
    }  

    public void setNo(String no) {  
        this.no = no;  
    }  

    public String getCaption() {  
        return caption;  
    }  

    public void setCaption(String caption) {  
        this.caption = caption;  
    }  
}  

解析xml的類如下:

[java] view plain copy

public class MainTest {  

    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        // 建立一個Digester對象  
        Digester digester = new Digester();  
        //指定它不要用DTD驗證XML文檔的合法性——這是因爲我們沒有爲XML文檔定義DTD  
        digester.setValidating(false);  
        // 從library標籤開始解析,並新建一個Library對象做爲根對象  
        digester.addObjectCreate("library", Library.class);  
        // 根據library標籤屬性值設置對象的屬性,一次可以設置多個屬性  
        digester.addSetProperties("library");  
        // 也可以用下面的方法,指定propertyName  
        // digester.addSetProperties("library", "name", "name");  

        // -----第1層元素開始  
        digester.addObjectCreate("library/book", Book.class);  
        //digester.addSetProperties("library/book");  
        // 可以用以下三條語句代替  
        digester.addCallMethod("library/book", "setBookInfo", 2);  
        digester.addCallParam("library/book", 0, "title");  
        digester.addCallParam("library/book", 1, "author");  
        /** 
         * addCallParam(String rule, int  paraIndex,String attributeName) 
         * 該方法與addCallMethod配合使用 
         * int paraIndex:表明需要填充的方法形參序號,從 0 開始,方法由addCallMethod指定 
         * String attributeName:指定標籤屬性名稱 
         */  


        // -----第2層元素開始  
        digester.addObjectCreate("library/book/chapter", Chapter.class);  
        /** addBeanPropertySetter()是將子節點轉換爲對象的屬性,這個方法還可以有第二個參數,當對象的屬性名和子節點的名字不一樣時用來指定對象的屬性名 
            該方法的作用及使用方法類似於addSetProperties,只不過它是用String rule規則所指定標籤的值(而不是標籤的屬性)來調用對象的setter*/  
        digester.addBeanPropertySetter("library/book/chapter/no");  
        // digester.addBeanPropertySetter("library/book/chapter/no", "no");  

        /** addCallMethod(String rule,String methodName, int  paraNumber) 方法 
         * 同樣是設置對象的屬性,但是方式更加靈活,不需要對象具有setter 
         * 當paraNumber = 0時,可以單獨使用(表明爲標籤的值來調用),不然需要配合addCallParam方法 
        */  
        // digester.addBeanPropertySetter("library/book/chapter/caption");  
        // 下面的方法,可以用來代替上一句,作用是一樣的   
        digester.addCallMethod("library/book/chapter/caption", "setCaption", 0);  

        // addSetNext()是說在再次遇到匹配節點後, 調用當前對象(Chapter類的對象)的父對象(Book類的對象)的方法,方法參數是當前層元素的對象  
        digester.addSetNext("library/book/chapter", "addChapter");  
        // -----第2層元素結束  

        digester.addSetNext("library/book", "addBook");  
        // -----第1層元素結束  

        try {  
            // 解析XML文件,並得到ROOT元素  
            Library library = (Library) digester.parse(MainTest.class.getResourceAsStream("books.xml"));  
            System.out.println(" 圖書館: " + library.getName());  
            System.out.println(" 共藏書: " + library.getBookList().size() + " 本 ");  
            System.out.println(" ***************************** ");  

            for (Book book : library.getBookList()) {  
                System.out.println(" 書名: " + book.getTitle() + "        作者: " + book.getAuthor());  
                System.out.println(" ------------------------------ ");  
                // 顯示章節  
                System.out.println(" 共 " + book.getChapters().size() + " 章 ");  
                for (Chapter chapter : book.getChapters()) {  
                    System.out.println(chapter.getNo() + ": " + chapter.getCaption());  
                }  
                System.out.println(" ------------------------------ ");  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } catch (SAXException e) {  
            e.printStackTrace();  
        }  
    }  
}  

例子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文件,可以知道有哪些標籤可以使用)

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE digester-rules PUBLIC  
   "-//Jakarta Apache //DTD digester-rules XML V1.0//EN"  
   "digester-rules.dtd">   
<digester-rules>    
    <object-create-rule pattern="library" classname="com.alibaba.chj.digester.Library" />    
    <set-properties-rule pattern="library">    
        <alias attr-name="name" prop-name="name" />    
    </set-properties-rule>    
    <pattern value="library/book">    
        <object-create-rule classname="com.alibaba.chj.digester.Book" />    
        <set-properties-rule />    
        <pattern value="chapter">    
            <object-create-rule classname="com.alibaba.chj.digester.Chapter" />    
            <bean-property-setter-rule pattern="no" propertyname="no" />  
            <bean-property-setter-rule pattern="caption" propertyname="caption" />  
            <set-next-rule methodname="addChapter" />    
        </pattern>       
        <set-next-rule methodname="addBook" />    
    </pattern>    
</digester-rules>    

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

public class MainTest {  

    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        try {      
            Digester digester = DigesterLoader.createDigester(DigesterXmlRuleTest.class.getResource("books-rule.xml"));      
            Library library = (Library) digester.parse(DigesterXmlRuleTest.class.getResourceAsStream("books.xml"));      
            System.out.println(" 圖書館: " + library.getName());  
            System.out.println(" 共藏書: " + library.getBookList().size() + " 本 ");  
            System.out.println(" ***************************** ");  

            for (Book book : library.getBookList()) {  
                System.out.println(" 書名: " + book.getTitle() + "        作者: " + book.getAuthor());  
                System.out.println(" ------------------------------ ");  
                // 顯示章節  
                System.out.println(" 共 " + book.getChapters().size() + " 章 ");  
                for (Chapter chapter : book.getChapters()) {  
                    System.out.println(chapter.getNo() + ": " + chapter.getCaption());  
                }  
                System.out.println(" ------------------------------ ");  
            }     
        } catch (IOException e) {  
            e.printStackTrace();  
        } catch (SAXException e) {  
            e.printStackTrace();  
        }   
    }  
}  

用於規則放在xml文件中,所以解析的類,顯得更加簡潔一些。

Digester FAQ: http://wiki.apache.org/commons/Digester/FAQ

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章