转载

Digester : a java object builder based xml

在Java编程中,配置文件大多数都是用xml文件来组织的。所以在Java语言中处理xml的工具就特别多。

在java中解析XML的几种方式,应该都是知道的。在这些解析技术的基础之上,又发展了几种优秀Object/XML关联的技术,例如有一种对象绑定技术(JAXB),再例如Digester。这里就来简单的了解一下Digester技术。

如果说对Digester运用最为高超的应用,非Tomcat莫属了。Tomcat中几个重要的配置文件(如server.xml、web.xml、context.xml),都是使用Digester来完成xml到Java对象的转换的。

Digester只需要设置好相应的处理规定,就可以得到想要的对象结构。它的最大优点就是可以自定义各种处理规则。然而,如果不能明白Digester的设计原理,那你就只使用Digester给你提供的几种规则了。

Digester例子

Java对象设计:

package com.fjn.frame.digester.list; public class XxObject {  private String id;  private String name;  private int num;  public String getId() {   return id;  }  public void setId(String id) {   this.id = id;  }  public String getName() {   return name;  }  public void setName(String name) {   this.name = name;  }  public int getNum() {   return num;  }  public void setNum(int num) {   this.num = num;  }  @Override  public String toString() {   return "id: " + id + "/tname: " + name + "/tnum: " + num;  } } 

这个类没有任何的含义,只是我随便写的而已。


一个简单的XML文件:

<?xml version="1.0" encoding="UTF-8"?> <XxObjects>  <XxObject id="id001" name="hello1" num="1" />  <XxObject id="id002" name="hello2" num="2" />  <XxObject id="id003" name="hello3" num="3" />  <XxObject id="id004" name="hello4" num="4" />  <XxObject id="id005" name="hello5" num="5" />  <XxObject id="id006" name="hello6" num="6" />  <XxObject id="id007" name="hello7" num="7" />  <XxObject id="id008" name="hello8" num="8" />  <XxObject id="id009" name="hello9" num="9" /> </XxObjects> 

接下来的任务就是将这个XML文件转换为一个对象集合了。

package com.fjn.frame.digester.list; import java.io.IOException; import java.util.List; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.digester3.Digester; import org.junit.Test; import org.xml.sax.SAXException; public class Convertor {     @Test     public void test() throws ParserConfigurationException, SAXException,      FactoryConfigurationError, IOException {  SAXParser parser = SAXParserFactory.newInstance().newSAXParser();  Digester digester = new Digester(parser);  digester.addObjectCreate("XxObjects", "java.util.LinkedList");  digester.addObjectCreate("XxObjects/XxObject",   "com.fjn.frame.digester.list.XxObject");  digester.addSetProperties("XxObjects/XxObject");  // digester.addSetNext("XxObjects/XxObject", "add");  digester.addRule("XxObjects/XxObject", new AddToCollectionRule());  List<XxObject> ret = digester.parse(Convertor.class   .getResourceAsStream("XxObjects.xml"));  for (XxObject obj : ret) {      System.out.println(obj);  }     } } 

AddToCollectionRule规则是我自定义的规则:  

package com.fjn.frame.digester.list; import java.util.Collection; import org.apache.commons.digester3.Rule; import org.xml.sax.Attributes; public class AddToCollectionRule extends Rule {  @Override  public void begin(String namespace, String name, Attributes attributes)    throws Exception {   Object top = this.getDigester().peek();   Object o = this.getDigester().peek(1);   if (o != null) {    Collection colls = (Collection) o;    System.out.println(top);    colls.add(top);   }  } } 

程序运行结果:  

id: id001 name: hello1 num: 1 id: id002 name: hello2 num: 2 id: id003 name: hello3 num: 3 id: id004 name: hello4 num: 4 id: id005 name: hello5 num: 5 id: id006 name: hello6 num: 6 id: id007 name: hello7 num: 7 id: id008 name: hello8 num: 8 id: id009 name: hello9 num: 9 id: id001 name: hello1 num: 1 id: id002 name: hello2 num: 2 id: id003 name: hello3 num: 3 id: id004 name: hello4 num: 4 id: id005 name: hello5 num: 5 id: id006 name: hello6 num: 6 id: id007 name: hello7 num: 7 id: id008 name: hello8 num: 8 id: id009 name: hello9 num: 9

使用Digester将xml转为java对象,就是这么简单。  

使用起来如此简单,它是怎么做到的呢?

Digester设计原理

如果是使用DOM解析的方式,我相信只要你了解DOM树,你就肯定能够完成这个轻松的任务,这也是程序员们更加容易接受DOM解析的原因。但是,设想一下,如果让你来将一个XML文件使用SAX解析的方式,转换为Java对象,你会怎么做呢?

我在看Digester的源码之前,就做过这样的设想。因为SAX解析的方式中,我们使用的最多的应当是startElement和endElement了。SAX解析是基于事件的,遇到一个开始元素,就执行startElement方法,遇到一个结束元素就执行endElement方法。

一般来说一个XML元素就对应一个Java对象,XML元素的属性就对应的是Java对象的属性。遇到一个元素开始,就可以根据元素名称来创建出对象。根据元素的属性来设置对象的属性。这个了是很简单的,但是在end一个元素后,也就是对象创建完成后,怎么来存储呢?总不能解析完成,生成了很多对象,我一个也拿不到,那解析它有何用呢。可以考虑一个复杂的XML,例如tomcat的server.xml文件,Server/Service/Engine等有对象是嵌套的,创建一个对象Service后,怎么将它设置到Server中呢?创建一个对象Engine后,怎么将它设置到Service中呢。完成这个工作,就必然要使用到一个数据结构:Stack。如果你能想到这里,Digester你就学会了80%了。

Java对象在startElement中创建并入栈,在endElement中完成所有操作并出栈。

例子说明

在上面的例子中,解析到根元素时,会创建出一个LinkedList,然后入栈。因为没有到结束元素,所以它肯定会在栈里,并且是在最下面。然后是解析每个XxObject了。在解析XxObject元素时,都要有3个操作,它们是依次进行的:

1)创建XxObject对象。入栈,那么这个对象肯定是在栈顶了。

2)设置属性。Digester使用的是BeanUtils工具来完成属性的设置的,所以java类在设计时, 是需要 getter和setter的。

3)调用我自定义的AddToCollection规则了。执行这个规则也很简单,取得当前对象(栈顶元素),再取出从栈顶起第二个元素,也就是LinkedList。然后就可以添加到集合中了。

4)endElement,对象出栈,栈中只剩下LinkedList对象。

最后遇到XxObejcts的end,它就是解析到最后一刻时栈底。返回的什就是栈底对象。所以结果就是一个List了。

我自定义的那个AddToCollection规则,只是想用于说明如何Digester的原理,以及如何使用自定义规则。其实像这个的常用的规则,Digester已经定义好了,SetNetRule。

到这里,这篇文章的主要内容已经说完了。至于如何使用Digester中的各种Rule,还需要靠自己去了解了。

下面附加一张类图:

Digester : a java object builder based xml

正文到此结束
Loading...