Digester解析xml文件

最近在看tomcat的源码,其使用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/driver'  
   
           'datasources/datasource' 
                'datasources/datasource/name' 
              'datasources/datasource/driver'  
   
 


例子1:

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



	
		
			1098
			1001
			province
			北京
			1867
		
		
			1099
			1098
			capital
			北京
			1868
			010
		
		
			4476
			1099
			county	
			北京市朝阳区
			1869
			010
		
		
			4477
			1099
			county
			北京市崇文区
			1870
			010
		
		
			4478
			1099
			county
			北京市大兴区
			1871
			010
		
	

此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如下:

 
 
      
          
             第一章 
             第一章的标题 
          
          
             第二章 
             第二章的标题 
          
      
      
          
             第一章 
             第一章的标题 
          
      

Library类如下:

public class Library {
    private String name;
    private List bookList = new ArrayList();
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public List getBookList() {
        return bookList;
    }
    
    public void addBook(Book book) {
        bookList.add(book);
    }
}

Book类如下:

public class Book {

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

    /**
     * 这个方法,用来演示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 getChapters() {
        return chapters;
    }

    public void setChapters(List 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的类如下:

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();
        }
    }
}

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类的代码,修改为:

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

你可能感兴趣的:(Java笔记)