在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给你提供的几种规则了。
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对象,就是这么简单。
使用起来如此简单,它是怎么做到的呢?
如果是使用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,还需要靠自己去了解了。
下面附加一张类图: