此篇为译文,原文请参考:
http://www.javaworld.com/article/2074684/learn-java/simplify-xml-file-processing-with-the-jakarta-commons-digester.html
注意:为了使用Digester,你必须将如下库文件(jar包)添加到classpath中:
(1) BeanUtils,
(2) Collections,
(3) Logging,
(4) An XML parser conforming to SAX (Simple API for XML) 2.0 or JAXP (Java API for XML Parsing) 1.1.
XML文件解析概述
解析XML文档有两种基本方式。第一种,DOM(Document Object Model)方式。使用这种方式,解析器会读取整个文档并创建一个与之对应的树形结构。第二种,SAX方式,以事件驱动来(events)解析文档。第一种方式易实现,但比起SAX方式,速度慢,耗费资源。Digester通过提供一个高层次的SAX事件接口来简化SAX解析方式。这个接口隐藏了XML文档导航的复杂性,使得开发人员可以将精力集中到处理XML数据上来。
Digester的概念
Digester引入了3个重要的概念:
(1) 元素匹配模式
(2) 处理规则
(3) 对象栈
元素匹配模式将XML元素和处理规则联系在一起。下面的例子展示了一些元素匹配模式所对应的XML层级(XML数据):
XML层级 元素匹配模式
<datasources> 'datasources'
<datasource> 'datasources/datasource'
<name/> 'datasources/datasource/name'
<driver/> 'datasources/datasource/driver'
</datasource>
<datasource> 'datasources/datasource'
<name/> 'datasources/datasource/name'
<driver/> 'datasources/datasource/driver'
</datasource>
</datasources>
如果找到一个元素匹配模式,那么与之对应的处理规则就被激活。在上面的例子中,与'datasources/datasource'模式相对应的规则被执行两次。处理规则定义了当Digester匹配到一个模式时将发生的事件。Digester内置了一些预定义的规则。你也可以编写org.apache.commons.digester.Rule的子类来实现自定义的规则。利用对象栈,你可以通过操控处理规则来获取最终的Java对象。对象可以被添加到栈中,也可以从栈中移除,既可以手动实现,也可以通过处理规则自动实现。
使用Digester
Digester通常用于解析XML配置文件。如下例所示,假设有一个XML配置文件,包含数据源配置信息。另有一个DataSource类,它包括一个默认构造函数和一些getter/setter方法,我们的目标是将XML文件中的信息转换为程序中的DataSource对象。
XML文件示例:
<?xml version="1.0"?>
<datasources>
<datasource>
<name>HsqlDataSource</name>
<driver>org.hsqldb.jdbcDriver</driver>
<url>jdbc:hsqldb:hsql://localhost</url>
<username>sa</username>
<password>lion</password>
</datasource>
<datasource>
<name>OracleDataSource</name>
<driver>oracle.jdbc.driver.OracleDriver</driver>
<url>jdbc:oracle:thin:@localhost:1521:orcl</url>
<username>scott</username>
<password>tiger</password>
</datasource>
</datasources>
DataSource类示例:
public class DataSource {
private String name;
private String driver;
private String url;
private String userName;
private String password;
public DataSource() {
System.out.println("call the default constructor");
}
public DataSource(String name, String driver, String url,
String userName, String password) {
this.name = name;
this.driver = driver;
this.url = url;
this.userName = userName;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("current name: " + name);
this.name = name;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
System.out.println("current driver: " + driver);
this.driver = driver;
}
public String getURL() {
return url;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
}
为了使用Digester,你需要先构造一个Digester实例,然后将你需要的对象放到Digester的对象栈中,添加处理规则,最后一步是解析文件,如下:
public class BadDigester {
public static void main(String[] args) throws IOException, SAXException {
// TODO Auto-generated method stub
Digester digester = new Digester();
digester.addObjectCreate("datasources/datasource", "DataSource");
digester.addCallMethod("datasources/datasource/name", "setName", 0);
digester.addCallMethod("datasources/datasource/driver", "setDriver", 0);
String path = SampleDigester.class.getClassLoader().getResource("datasource.xml").getPath();
InputStream is = new FileInputStream(path);
BufferedInputStream bis = new BufferedInputStream(is);
digester.parse(bis);
bis.close();
}
} // BadDigester
在这个例子中,addObjectCreate()方法为'datasources/datasource'模式添加一个对象创建规则。这个规则创建一个DataSource类的实例并把这个实例放到Digester的对象栈中。然后,addCallMethod()方法为两个模式添加调用方法规则,这个规则调用处于对象栈顶的对象的方法。addCallMethod()的最后一个参数指定了传给被调方法的附加参数的个数。这里是0,因此只有匹配元素的主体被传入方法中。如果将这段代码应用于上述XML文件,将会看到:
一个DataSource对象被创建,并被添加到对象栈中,
这个对象的setName方法被调用,参数是HsqlDataSource,
这个对象的setDriver方法被调用,参数是org.hsqldb.jdbcDriver。
在datasource元素结尾处,对象被弹出对象栈,然后开始一个新的处理周期。
这段示例存在一个问题,对象创建规则会在元素结尾处将与之对应的对象弹出对象栈。这样,当Digester解析完文档后,只有最后一个对象被保留下来。为解决这个问题,可以在解析开始前,往对象栈内添加一个对象,然后调用这个对象的方法来创建你实际需要的对象,下面是一个示例类:
public class SampleDigester {
private Hashtable dataSources = new Hashtable();
public static void main(String[] args) {
SampleDigester sample = new SampleDigester();
try {
sample.run();
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() throws IOException, SAXException {
Digester digester = new Digester();
digester.push(this);
digester.addCallMethod("datasources/datasource", "addDataSource", 5);
digester.addCallParam("datasources/datasource/name", 0);
digester.addCallParam("datasources/datasource/driver", 1);
digester.addCallParam("datasources/datasource/url", 2);
digester.addCallParam("datasources/datasource/username", 3);
digester.addCallParam("datasources/datasource/password", 4);
String path = SampleDigester.class.getClassLoader().getResource("datasource.xml").getPath();
InputStream is = new FileInputStream(path);
BufferedInputStream bis = new BufferedInputStream(is);
digester.parse(bis);
bis.close();
System.out.println("total dataSources: " + dataSources.size());
}
public void addDataSource(String name, String driver, String url, String userName, String password) {
DataSource dataSource = new DataSource(name, driver, url, userName, password);
dataSources.put(name, dataSource);
System.out.println("DataSource added: " + name);
}
}
在这个类中,当'datasources/datasource'模式匹配时,addDataSource()方法会被调用。addCallParam()方法会添加调用参数规则,这个规则会把匹配元素的主体作为addDataSource()方法的参数。在addDataSource()方法中,你可以创建真正的DataSource对象,并将它们保存起来。
结语
虽然最初开发Digester是为了简化解析XML配置文件,但它可以用于任何你想将XML文件映射为Java对象的情况。本文仅仅给出一个简介。获取更多信息,可以参考Apache Commons官方网站。另外,原文还提供了一些有关Digester的实用参考示例和源码的链接。