使用Apache Commons Digester
本文主要参考了The serverside上的一篇文章。描述了2种典型的使用digester的方法,以及所需要注意的地方。详细内容请参见正文。
简化 xml 文件处理,它依赖的组件: BeanUtils 、 Logging 、 Collections 。
1. 在 Digester 的内部采用 SAX 来解析 XML 文件。为了能够正确的使用它,必须在解析之前进行相应的设置。同时,在解析 xml 文件的过程中,它使用 Stack 来保存和检索这个期间产生的对象。
2. 为了简化使用,它通过匹配模式 来定位要解析的 xml 标签。匹配模式的例子如下:
xml 文件:
<?xml version=”1.0″?>
<students>
<student>
<name>Java Boy</name>
<course>JSP</course>
</student>
<student>
<name>Java Girl</name>
<course>EJB</course>
</student>
</students>
每个标签与相应的匹配模式对应如下表:
标签 |
匹配模式 |
<students> |
students |
<student> |
students/student |
<name> |
students/student/name |
<course> |
students/student/course |
如果将 xml 文件结构视为一颗树的话,那么每个标签的匹配模式就是从根元素到这个元素的路径。除了使用具体的标签,还可以使用通配符。
3. 使用匹配模式可以很方便的定位需要处理的元素,为了处理这些元素,需要定义处理规则 。规则在匹配模式被找到时起作用。所有的规则都是从 org.apache.commons.digester.Rule 派生的。所有已定义的 Rule 对象,可以在 org.apache.commons.digester 中找到。常用的规则:
- ObjectCreate ,创建对象实例。
- SetProperties ,将标签属性( Attribute )与要创建的对象的属性相关联。
- BeanPropertySetter ,将标签所包含标签与要创建的对象的属性相关联。
- SetNext ,设置遇到下一个标签时的动作。
- CallMethod ,设置当匹配模式被找到时要调用的方法。
- CallParam ,设置对应的 callMethod 中指定方法所需要的参数值。
以正确的顺序调用 Digester 方法是成功使用 Digester 处理 XML 文件的关键。使用步骤如下:
1. 创建 org.apache.commons.digester.Digester 实例并配置,包括设置实现 Digester Rule 的对象。
2. 使用 Digester 的 push 方法在 Digester 使用的 stack 中放置一个初始对象。在解析 xml 文件的过程中, Digester 使用 stack 来保存它所找到的对象。第一个对象在遇到第一个标签时被放置到 stack 中,当最后一个标签处理完毕时被弹出。为了最后能检索到这个对象,因此需要一个初始对象来保留一个指向它的引用。
3. 注册匹配模式和 rule 。
4. 调用 parse 来解析 xml 文件。
1. 简单例子,从一个 xml 文件中创建对象。
XML 文件 :
<?xml version=”1.0″ encoding=”UTF-8″?>
<students>
<student>
<name>Java Boy</name>
<course>JSP</course>
</student>
<student>
<name>Java Girl</name>
<course>EJB</course>
</student>
</students>
要创建的对象 :
public class Student {
private String name;
private String course;
……
}
解析 :
public class DigesterStudy {
private Vector students;
public DigesterStudy(){
students= new Vector( 5);
}
public static void main(String[] args) {
DigesterStudy ds= new DigesterStudy();
ds.digest();
}
public void digest(){
// 创建实例
Digester digester= new Digester();
// 将初始对象压入 digester 的 stack
digester.push( this);
// 指明匹配模式和要创建的类
digester.addObjectCreate( “students/student”, Student.class);
// 设置对象属性
digester.addBeanPropertySetter( “students/student/name”);
digester.addBeanPropertySetter( “students/student/course”);
// 当移动到下一个标签中时的动作
digester.addSetNext( “students/student”, “addStudent”);
try {
// 解析
DigesterStudy ds= (DigesterStudy)digester.parse( getClass()
.getClassLoader()
.getResourceAsStream( “students.xml”));
System.out.print( ds);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addStudent( Student student){
students.add( student);
}
public String toString(){
return students.toString();
}
}
2. 复杂例子,从 xml 文件中创建相互之间有关系的对象。
XML 文件 :
<?xml version=”1.0″ encoding=”UTF-8″?>
<academy name=”JAcademy” >
<student name=”JavaBoy” division=”A”>
<course>
<id>C1</id>
<name>JSP</name>
</course>
<course>
<id>C2</id>
<name>Servlets</name>
</course>
</student>
<student name=”JavaGirl” division=”B”>
<course>
<id>C3</id>
<name>EJB</name>
</course>
</student>
<teacher name=”JavaGuru”>
<certification>SCJP</certification>
<certification>SCWCD</certification>
</teacher>
<teacher name=”JavaMaster”>
<certification>OCP</certification>
<certification>SCJP</certification>
<certification>SCEA</certification>
</teacher>
</academy>
要创建的对象 :
public class Academy {
private String name;
private Vector students;
private Vector teachers;
……
}
public class Student {
private String name;
private String division;
private Vector courses;
……
}
public class Course {
private String id;
private String name;
……
}
public class Teacher {
private String name;
private Vector certifications;
……
}
其中的 <certification> 在程序中对应一个字符串,故而没有列出。
解析 :
public void digest(){
Digester digester= new Digester();
// 注意,此处并没有象上例一样使用 push ,是因为此处从根元素创建了一个对 // 象实例
digester.addObjectCreate( “academy”, Academy.class);
// 将 < academy > 的属性与对象的属性关联
digester.addSetProperties( “academy”);
digester.addObjectCreate( “academy/student”, Student.class);
digester.addSetProperties( “academy/student”);
digester.addObjectCreate( “academy/student/course”, Course.class);
digester.addBeanPropertySetter( “academy/student/course/id”);
digester.addBeanPropertySetter( “academy/student/course/name”);
digester.addSetNext( “academy/student/course”, “addCourse”);
digester.addSetNext( “academy/student”, “addStudent”);
digester.addObjectCreate( “academy/teacher”, Teacher.class);
digester.addSetProperties( “academy/teacher”);
// 当遇到 academy/teacher/certification 时,调用 addCertification
digester.addCallMethod( “academy/teacher/certification”,
“addCertification”, 1);
// 设置 addCertification 的参数值,此处的 0 表示这个元素体的第一个值
// 为参数值传入 addCertification 。在此处,即为 <certification> 的值。
// (因为就只有一个)
digester.addCallParam( “academy/teacher/certification”, 0);
digester.addSetNext( “academy/teacher”, “addTeacher”);
try {
Academy a= (Academy)digester.parse( getClass()
.getClassLoader()
.getResourceAsStream( “students.xml”));
System.out.print( a);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
3. 从配置文件中创建 Digester ,通过 DigesterLoader 可以从配置文件中创建 Digester 实例,从而省去类似于上例手工配置 Digester 的代码。
Digester 的配置文件,对应上例的配置文件如下:
<?xml version=”1.0″?>
<digester-rules>
<pattern value=”academy”>
<object-create-rule classname=”Academy” />
<set-properties-rule />
<pattern value=”student”>
<object-create-rule classname=”Student” />
<set-properties-rule />
<pattern value=”course”>
<object-create-rule classname=”Course” />
<bean-property-setter-rule pattern=”id”/>
<bean-property-setter-rule pattern=”name”/>
<set-next-rule methodname=”addCourse” />
</pattern>
<set-next-rule methodname=”addStudent” />
</pattern>
<pattern value=”teacher”>
<object-create-rule classname=”Teacher” />
<set-properties-rule />
<call-method-rule pattern=”certification” methodname=”addCertification”
paramcount=”1″ />
<call-param-rule pattern=”certification” paramnumber=”0″/>
<set-next-rule methodname=”addTeacher” />
</pattern>
</pattern>
</digester-rules>
使用配置文件之后,上例的代码变为:
Digester digester = DigesterLoader.createDigester(
this.getClass().getClassLoader().getResource(“academyRules.xml”));
Academy a= (Academy)digester.parse( getClass()
.getClassLoader().getResourceAsStream( “students.xml”));
在使用 Digester 需要注意的地方:
- 调用顺序,正确的调用顺序才能得出正确的结果。方法调用顺序基本和标签在 xml 文件中的层次关系相对应。基本的顺序是:先创建对象;然后设置属性;随后处理子元素;最后设置遇到下一个元素所对应的动作 。对于子元素的处理,是同样的过程。
- 正确的使用初始对象。对比上面 2 个例子,之所以在第一个例子中显示的调用了 digester 的 push 方法,其原因就在于我们并没有如第二个例子一样用 xml 的 root 元素创建一个实例。如果不显式的调用,我们将会丢失这个元素的引用,那么也就无法得到后续的对象。
- digester 的 addSetNex 方法中所指定的方法实际上是包含匹配模式对应标签的父标签对应对象的方法。在上两个例子中 addStudent ,都是包含 Student 对象的那个对象的方法。对于第一个例子,是 DigesterStudy ;对于第二个例子,是 Academy 。而且它的位置通常是在创建对象语句组的最后,与 addObjectCreate 相对应。在这 2 个语句之间的代码中所指定的方法都是所创建对象的方法。而且它们的顺序与匹配模式所对应的标签的顺序必须是一致的。
- 使用配置文件来创建 digester ,这样会带来很大的灵活性。
原文链接: http://blog.donews.com/zoulinzhi/archive/2004/07/01/36278.aspx