XML类包含了与Java、XML技术相关的类,包括:Betwixt,Digester,Jelly,和JXPath。
2.1 Betwixt
■ 概况:实现XML和JavaBean的映射。
■ 官方资源:主页,二进制,源代码。
■ 何时适用:当你想要以灵活的方式实现XML和Bean的映射,需要一个数据绑定框架之时。
■示例应用:BetwixtDemo.java,Mortgage.java,mortgage.xml。要求CLASSPATH中必须包含commons-betwixt-1.0-alpha-1.jar、commons-logging.jar、commons-beanutils.jar、commons-collections.jar、以及commons-digester.jar。
■ 说明:
如果你以前曾经用Castor绑定数据,一定会欣赏Betwixt的灵活性。Castor适合在一个预定义模式(Schema)的基础上执行Bean和XML之间的转换;但如果你只想执行数据和XML之间的转换,最好的选择就是Betwixt。Betwixt的特点就是灵活,能够方便地将数据输出成为人类可阅读的XML。
Betwixt的用法相当简单。如果要把Bean转换成XML,首先创建一个BeanWriter的实例,设置其属性,然后输出;如果要把XML转换成Bean,首先创建一个BeanReader的实例,设置其属性,然后用Digester执行转换。
将Bean转换成XML:
// 用Betwixt将Bean转换成XML必须有BeanWriter的实例。
// 由于BeanWriter的构造函数要求有一个写入器对象,
// 所以我们从创建一个StringWriter开始
StringWriter outputWriter = new StringWriter();
// 注意输出结果并不是格式良好的,所以需要在开始位置
// 写入下面的内容:
outputWriter.write("<?xml version='1.0' ?>");
// 创建一个BeanWriter
BeanWriter writer = new BeanWriter(outputWriter);
// 我们可以设置该写入器的各种属性。
// 下面的第一行禁止写入ID,
// 第二行允许格式化输出
writer.setWriteIDs(false);
writer.enablePrettyPrint();
// 创建一个Bean并将其输出
Mortgage mortgage = new Mortgage(6.5f, 25);
// 将输出结果写入输出设备
try {
writer.write("mortgage", mortgage);
System.err.println(outputWriter.toString());
} catch(Exception e) {
System.err.println(e);
}
将XML转换成Bean:
// 用Betwixt来读取XML数据并以此为基础创建
// Bean,必须用到BeanReader类。注意BeanReader类扩展了
// Digester包的Digester类。
BeanReader reader = new BeanReader();
// 注册类
try {
reader.registerBeanClass(Mortgage.class);
// 并解析它…
Mortgage mortgageConverted =
(Mortgage)reader.parse(new File("mortgage.xml"));
// 检查转换得到的mortgage是否包含文件中的值
System.err.println("Rate: " + mortgageConverted.getRate() +
", Years: " + mortgageConverted.getYears());
} catch(Exception ee) {
ee.printStackTrace();
}
注意,通过BeanReader注册类时,如果顶层元素的名称和类的名称不同,必须用另一个方法注册并指定准确的路径,如reader.registerBeanClass("toplevelelementname", Mortgage.class)。
2.2 Digester
■ 概况:提供友好的、事件驱动的高级XML文档处理API。
■ 官方资源:主页,二进制,源代码。
■ 何时适用:当你想要处理XML文档,而且希望能够根据XML文档中特定的模式所触发的一组规则来执行某些操作时。
■ 示例应用:DigesterDemo.java、 Employee.java、 Company.java、 rules.xml以及company.xml。要求CLASSPATH中必须包含commons-digester.jar、 commons-logging.jar、 commons-beanutils.jar以及commons-collections.jar。
■ 说明:
Digester在解析配置文件的时候最为有用。实际上,Digester最初就是为读取Struts配置文件而开发的,后来才移到Commons包。
Digester是一个强大的模式匹配工具,允许开发者在一个比SAX或DOM API更高的层次上处理XML文档,当找到特定的模式(或找不到模式)时能够触发一组规则。使用Digester的基本思路是:首先创建一个Digester的实例,然后用它注册一系列模式和规则,最后将XML文档传递给它。此后,Digester就会分析XML文档,按照注册次序来触发规则。如果XML文档中的某个元素匹配一条以上的规则,所有的规则会按照注册次序被依次触发。
Digester本身带有12条预定义的规则。当XML文档中找到一个特定的模式时,想要调用某个方法吗?很简单,使用预定义的CallMethodRule!另外,你不一定要使用预定的规则,Digester允许用户通过扩展Rule类定义自己的规则。
在指定模式时,元素必须用绝对名称给出。例如,根元素直接用名称指定,下一层元素则通过"/"符号引出。例如,假设company是根元素,company/employee就是匹配其中一个子元素的模式。Digester允许使用通配符,例如*/employee将匹配XML文档内出现的所有employee元素。
找到匹配的模式时,关联到该匹配模式的规则内有四个回调方法会被调用,它们是:begin,end,body,和finish。这些方法被调用的时刻正如其名字所示,例如调用begin和end的时刻分别是遇到元素的开始标记和结束标记之时,body是在遇到了匹配模式之内的文本时被调用,finish则是在全部对匹配模式的处理工作结束后被调用。
最后,模式可以在一个外部的规则XML文档内指定(利用digester-rules.dtd),或者在代码之内指定,下面要使用的是第一种办法,因为这种办法比较常用。
使用Digester之前要创建两个XML文档。第一个就是数据或配置文件,也就是我们准备对其应用规则的文件。下面是一个例子(company.xml)
<?xml version="1.0" encoding="gb2312"?>
<company>
<name>我的公司</name>
<address>中国浙江</address>
<employee>
<name>孙悟空</name>
<employeeNo>10000</employeeNo>
</employee>
<employee>
<name>猪八戒</name>
<employeeNo>10001</employeeNo>
</employee>
</company>
第二个文件是规则文件rules.xml。rules.xml告诉Digester要在company.xml中查找什么、找到了之后执行哪些操作:
<?xml version="1.0" encoding="gb2312"?>
<digester-rules>
<!-- 创建顶层的Company对象 -->
<object-create-rule pattern="company" classname="Company" />
<call-method-rule pattern="company/name" methodname="setName"
paramcount="0" />
<call-method-rule pattern="company/address"
methodname="setAddress" paramcount="0" />
<pattern value="company/employee">
<object-create-rule classname="Employee" />
<call-method-rule pattern="name" methodname="setName"
paramcount="0" />
<call-method-rule pattern="employeeNo" methodname=
"setEmployeeNo" paramcount="0" />
<set-next-rule methodname="addEmployee" />
</pattern>
</digester-rules>
这个文件有哪些含义呢?第一条规则,<object-create-rule pattern="company" classname="Company" />,告诉Digester如果遇到了模式company,则必须遵从object-create-rule,也就是要创建一个类的实例!那么要创建的是哪一个类的实例呢?classname="Company"属性指定了类的名称。因此,解析company.xml的时候,当遇到顶级的company元素,等到object-create-rule规则执行完毕,我们就拥有了一个Digester创建的Company类的实例。
现在要理解call-method-rule规则也应该不那么困难了,这里call-method-rule的功能是在遇到company/name或company/address模式时调用一个方法(方法的名字通过methodname属性指定)。
最后一个模式匹配值得注意,它把规则嵌套到了匹配模式之中。两种设定规则和模式的方式都是Digester接受的,我们可以根据自己的需要任意选择。在这个例子中,模式里面定义的规则在遇到company/employee模式时创建一个Employee类的对象,设置其属性,最后用set-next-rule将这个雇员加入到顶层的Company。
创建好上面两个XML文件之后,只要用两行代码就可以调用Digester了:
Digester digester = DigesterLoader.createDigester(rules.toURL());
Company company = (Company)digester.parse(inputXMLFile);
第一行代码装入规则文件,创建一个Digester。第二行代码利用该Digester来应用规则。请参见本文后面提供的DigesterDemo.java完整源代码。
2.3 Jelly
■ 概况:一种基于Java和XML的脚本语言。
■ 官方资源:主页,二进制,源代码。
■ 何时适用:简单地说,当你想要一种灵活的、可扩展的XML脚本工具之时。
■ 示例应用:JellyDemo.java,jellydemo.xml以及TrivialTag.java。要求CLASSPATH中必须有commons-jelly-1.0-dev.jar、dom4j.jar、commons-logging.jar、commons-beanutils.jar以及commons-collections.jar。
■ 说明:
要说清楚Jelly到底是什么以及它扮演着哪种角色是件很不容易的事情。Jelly试图提供一个通用的XML脚本引擎,这种脚本引擎是可以由开发者通过定制动作和标记扩展的,XML文档之中的元素映射到JavaBean,而XML元素的属性映射到JavaBean的属性。从某种意义上说,Jelly是一种结合了Betwixt和Digester的工具,但Jelly更强大,具有更好的可扩展性。
一个Jelly系统由多个组件构成。第一个组件是Jelly脚本,它是一种由Jelly引擎解析的XML文档,经过解析的XML文档元素被绑定到Jelly标记动态处理。第二个组件是Jelly标记,它是一种实现了Jelly的Tag接口的JavaBean,凡是Jelly标记都可以实现doTag方法,这个doTag方法就是当脚本引擎遇到XML文档中的特定元素时所执行的方法。Jelly正是通过这一机制实现动态的脚本处理能力,从某种意义上看,有点类似于Digester的工作机制。
Jelly带有许多预定义的标记,其中部分标记提供核心Jelly支持,其他标记用来提供解析、循环、条件执行代码等方面的支持。另外,Jelly还为Ant任务提供了广泛的支持。
要在Java应用程序中使用Jelly,首先要创建一个JellyContext的实例,例如:JellyContext context = new JellyContext();。我们可以把JellyContext对象看成是一个编译和运行Jelly脚本的运行环境。有了JellyContext就可以运行Jelly脚本。JellyContext的输出实际上是一个XMLOutput类的实例:context.runScript(new File("jellydemo.xml"), output);。
创建自定义标记时,我们既可以覆盖上面提到的doTag方法(如下面的例子所示),或者提供一个执行方法,如invoke()或run():
public void doTag(XMLOutput output) throws Exception {
// 在这里加入要执行的操作,
// 例如设置属性、访问文件系统等…
this.intProp = 3;
}
下面提供了一个定义Jelly脚本的XML文件示例:
<j:jelly xmlns:j="jelly:core" xmlns:define="jelly:define"
xmlns:tr="trivialTag">
<define:taglib uri="trivialTag">
<define:jellybean name="trivial" className="TrivialTag" />
</define:taglib>
<tr:trivial intProp="1" stringProp="ball">Hello World</tr:trivial>
</j:jelly>
这个例子用到jelly:define和jelly:core标记,以及一个trivialTag标记。当遇到trivial标记实例时,Jelly创建相应的JavaBean的实例,执行doTag方法(或者也可以是一个run或invoke之类可调用的方法)。
Jelly还有许多其他功能,它既可以直接从命令行或Ant脚本运行,也可以嵌入到应用程序的代码之内,请参见Jelly文档了解详情。
2.4 JXPath
■ 概况:Java中的XPath解释器。
■ 官方资源:主页,二进制,源代码。
■ 何时适用:当你想要在JavaBean、DOM或其他对象构成的结构中应用XPath查询之时。
■ 示例应用:JXPathDemo.java,Book.java,Author.java。要求CLASSPATH必须包含commons-jxpath-1.1.jar。
■ 说明:
下面的说明要求读者已具备基本的XPath知识。
XPath是一种查询XML文档的语言,JXPath将同一概念应用到了其他Java对象的查询,诸如JavaBean、Collection、Array和Map等。
JXPathContext是JXPath中的核心类,它利用一个工厂方法来定位和创建一个上下文的实例。由于有了这一机制,必要时开发者可以插入一个新的JXPath的实现。要使用JXPathContext,只要简单地向它传递一个JavaBean、Collection或Map,例如:JXPathContext context = JXPathContext.newContext(book);。
利用JXPathContext可执行许多任务。例如访问属性或嵌套属性,当然还可以设置属性:
System.err.println(context.getValue("title"));
System.err.println(context.getValue("author/authorId"));
context.setValue("author/authorId", "1001");
利用JXPath还可以查找其他类型的对象,不过创建上下文对象的方式都一样,都是用上面介绍的静态方法获得一个新的上下文,传入想要查询的对象。