Digester的基于XML规则集的详细介绍

Digester的基于XML规则集的详细介绍

用于Commons Digester的基于XML的规则集
Commons Digester 1.6提供了将XML转化为对象的最简单的方法。Digester已经由O'Reilly网站上的两篇文章介绍过了:“学习和使用Jakarta Digester”,作者是Philipp K. Janert,和“使用Jakarta Commons, 第二部分”,作者是Vikram Goyal。两篇文章都演示了XML规则集的使用,但如何在XML中定义规则集并没有理解。大多所见到的Digester的使用是程序化地定义规则集,以已编译的形式。你应该避免硬编码的Digester规则,特别是当你可以将映射信息存储在外部文件中或一个类路径资源中时。外部化一个Digester规则可以更好地适应一个演化中的XML文档结构或者说一个演化中的对象模型。

为了演示在XML中定义规则集与硬编码的规则集之间的区别,考虑系统解析XML给一个Person bean,包括在下面定义的属性—id, name和age。

package org.test;

public class Person {
public String id;
public String name;
public int age;

public Person() {}

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 getAge() { return age; }
public void setAge(int age) {
this.age = age;
}
}


确认你的应用需要解析一个包含了多个person元素的XML文件。下面的XML文件,data.xml,包含了两个person元素,你想要把它们解析到Person对象中:

<people>
<person id="1">
<name>Tom Higgins</name>
<age>25</age>
</person>
<person id="2">
<name>Barney Smith</name>
<age>75</age>
</person>
<person id="3">
<name>Susan Shields</name>
<age>53</age>
</person>
</people>


你希望如果结构和XML文件的内容在未来几个月中变化,你不需要在已编译的Java代码中硬编码XML文件的结构。为了做到这一点,你需要在一个XML文件中定义Digester的规则,并且它可以作为一种资源从类路径中装入。下面的XML文档,person-rules.xml,映射person元素到Person bean:
<digester-rules>
<pattern value="people/person">
<object-create-rule classname="org.test.Person"/>
<set-next-rule methodname="add" 
paramtype="java.lang.Object"/>
<set-properties-rule/>
<bean-property-setter-rule pattern="name"/>
<bean-property-setter-rule pattern="age"/>
</pattern>
</digester-rules>


上述所做的是指示Digester创建一个新的Person实例,当它遇到一个person元素时,调用add()来将Person对象加入到一个ArrayList中,设置person元素中相匹配的属性,并从下一级元素name和age中设置name和age的属性。

现在你已经看到了Person类,会被解析的文档,和以XML的形式定义的Digester规则。现在你需要创建一个由person-rules.xml定义了规则的Digester的实例。下面的代码创建 了一个Digester,通过将person-rules.xml的URL传递给DigesterLoader

既然person-rules.xml文件是与解析它的类在同一个包内的类路径资源,URL可以通过getClass().getResource()来得到。DigesterLoader然后解析规则并将它加到新创建的Digester上:

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;

// 从XML规则集中配置Digester
URL rules = getClass().getResource("./person-rules.xml");
Digester digester = 
DigesterLoader.createDigester(rules);

// 将空的List推入到Digester的堆栈
List people = new ArrayList();
digester.push( people );

// 解析XML文档
InputStream input = new FileInputStream( "data.xml" );
digester.parse( input );


一旦Digester完成对data.xml的解析,三个Person对象将会在ArrayList people中。
与将规则定义在XML不同的方法是使用简便的方法将它们加入到一个Digester实例中。大多数文章和例子都用这种方法,使用addObjectCreate() 和 addBeanPropertySetter()这样的方法来将规则加入中Digester上。下面的代码加入了与定义在person-rules.xml中相同的规则:

digester.addObjectCreate("people/person", 
 Person.class);
digester.addSetNext("people/person", 
"add", 
"java.lang.Object");
digester.addBeanPropertySetter("people/person", 
 "name");
digester.addBeanPropertySetter("people/person", 
 "age");



如果你曾经发现自己正在用一个有着2500行代码的类,用SAX来解析一个巨大的XML文档,或者使用DOM或JDOM的完整的一个集合类,你就会理解XML的解析比它应该做的要复杂的多,就大多数情况来说。如果你正在建一个有着严格的速度和内存要求的高效的系统,你会需要SAX解析器的速度。如果你需要DOM级别3的复杂度,你会需要像Apache Xerces的解析器。但如果你只是简单的试图将几个XML文档解析到对象中去的话,看一下Commons Digester, 并把你的规则定义在一个XML文件中。

任何时候你都应该将配置信息从硬编码中移出来。我会建议你在一个XML文件中定义规则并从文件系统或类路径中装入它。这样可以使你的程序更好地适应XML文档以及对象模型的变化。有关在XML文件中定义Digester规则的更多的资料,参看Jakarta Commons Cookbook一书的6.2节,“将XML文档转换为对象”