一、简介
Digester本来仅仅是Jakarta Struts中的一个工具,用于处理struts-config.xml配置文件。显然,将XML文件转换成相应的Java对象是一项很通用的功能,这个工具理应具有更广泛的用途,所以很快它就在Jakarta Commons项目(用于提
供可重用的Java组件库)中有了一席之地。
如今Digester随着Struts的发展以及其的公用性而被提到commons中独自立项,是apache的一个组件 apache commons-digester.jar,通过它可以很方便的从xml文件生成java对象.你不用再象以前通过jdom或者Xerces去读取一个
document对象.(jdom和Xerces仍然有它们的用武之地及强大之处,在其它应用里你也少不了它们) 。
二、工作原理
Digester由"事件"驱动,通过调用预定义的规则操作对象栈,将XML文件转换为Java对象。工作原理如下:
Digester底层采用SAX解析XML文件,所以很自然的,对象转换由"事件"驱动,即在识别出特定XML元素时(实际被细分为begin、body、end、finish四个时点),将执行特定的动作,比如创建特定的Java对象,或调用特定对象的方法
等。此处的XML元素根据匹配模式(matching pattern)识别,而相关操作由规则(rule)定义。在转换过程中,Digester维持了一个对象栈,可以看作对象转换的工作台,用来存放转换中生成的、或是为转换临时创建的Java对象。对输入XML
文件作了一趟完整的扫描后,对象栈的栈顶元素即为目标对象。由于Digester屏蔽了SAX解析的细节,使用者仅需关注转换操作本身,大大简化了转换操作。
三、Hello World
1. 用来演示的两个类 Foo.java 和 Bar.java
Foo.java
package com.desmond.generator.run.digester;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Foo {
private String name;
private List<Bar> barList = new ArrayList<Bar>();
public void addBar( Bar bar ) {
barList.add(bar);
}
public Bar findBar( int id ) {
Bar bar = null;
for(Bar b : this.barList) {
if(id == b.getId()) {
bar = b;
break;
}
}
return bar;
}
public Iterator<Bar> getBars() {
return this.barList.iterator();
}
public String getName() {
return this.name;
}
public void setName( String name ) {
this.name = name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Foo [name=" + name + ", barList=");
Iterator ite = this.getBars();
while(ite.hasNext()) {
sb.append("\r\t\t" + ite.next().toString());
}
return sb.toString();
}
}
Bar.java:
package com.desmond.generator.run.digester;
public class Bar {
private int id;
private String title;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Bar [id=" + id + ", title=" + title + "]";
}
}
2.需要被解释的XML文件 foobar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<foo name="The Parent">
<bar id="123" title="The First Child" />
<bar id="456" title="The Second Child" />
</foo>
3.测试类 Test.java
package com.desmond.generator.run.digester;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.digester.Digester;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
import com.desmond.generator.util.GeneratorHelper;
public class Test {
private static Logger log = Logger.getLogger(Test.class.getName());
public static void main(String[] args) {
Digester digester = new Digester();
digester.setValidating(false);
digester.addObjectCreate("foo", "com.desmond.generator.run.digester.Foo");
digester.addSetProperties("foo");
digester.addObjectCreate("foo/bar", Bar.class);
digester.addSetProperties("foo/bar");
digester.addSetNext("foo/bar", "addBar", "com.desmond.generator.run.digester.Bar");
InputStream input = GeneratorHelper.class.getClassLoader().getResourceAsStream(
"com/desmond/generator/run/digester/foobar.xml");
try {
Foo foo = (Foo) digester.parse(input);
log.info(foo);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.流程
a.当遇到最外层元素<foo>时,创建一个 com.desmond.generator.run.digester.Foo 对象新的实例(即代码"digester.addObjectCreate("foo", "com.desmond.generator.run.digester.Foo");"),并且把他压入对象栈;当遇到</foo>时,这个Foo对象将会出栈。
b."digester.addSetProperties("foo");" 为这个刚刚被创建的对象的属性设值(根据XML文件<foo>元素的值来设定)。
c.当遇到嵌套元素<bar>时,为Bar创建一个新的实例,压入对象栈中,当遇到</bar>时,把这个对象抛出栈。
d."digester.addSetProperties("foo/bar");",为Bar这个对象的属性设值(根据XML文件<foo>元素的值来设定)。注意,设值的时候,类型会自动转换(比如bar中的id 由String -->int),
用到了apache的另一个project "commons-beanutils".
e."digester.addSetNext("foo/bar", "addBar", "com.desmond.generator.run.digester.Bar");", 意思为调用对象栈中"next-to-top"元素(本例中就是Foo对象的实例)的addBar方法,
并把对象栈中栈顶对象作为参数传给他,注意,这个对象必须是“com.desmond.generator.run.digester.Bar”类型的。
f.一旦解析完成,对象栈中的第一个元素(本例中为Foo的实例)将会返回给你,他的属性和子类都是有值的。
5.Output:
Foo [name=The Parent, barList=
Bar [id=123, title=The First Child]
Bar [id=456, title=The Second Child]
]