Digester是apache开源项目Commons中的一个子项目,是一款解析处理XML文档的工具。现在Java领域中流传了很多有关处理XML文档解析的工具,除官方(Sun)的标准的SAX(最新版本2.0),DOM(最新版本3.0,在Tiger版本中集成)外[JAXP只是Sun定义的一组规范接口],其他开源不泛多多,比如Jdom,Dom4j,Castor等等,包括这款Apache的digester。说到这里,你不得不佩服开源组织的强大智慧的结晶,digester处理XML文档基于XML节点树Path的规则,实在给人一种赏心悦目之感,这也是偶一直对其情有独钟的最大理由。废话免了,转入正题。
刚说到Digester处理是基本类似于XML文档树节点遍历的规则来进行处理,底层处理是采用了SAX,基于事件驱动的模式。举个例子:
<Company>
<Technology>
<name length="4">Corx</name>
<date>2005.06.27</date>
</Technology>
<Product>
<name length="4">Kxcp</name>
<date>2004.12.29</date>
</Product>
</Company>
在digester中,定义了一些规则(rule),对遍历的节点path预先对应好要处理的规则,即当解析器遍历到某个节点的时候,如果发现当前节点有对应的处理规则,调用相应的rule进行处理。举个例子:
Company/Technology -> ObjectCreatedRule //对象创建规则
Company/Technology/name -> BeanPropertySetterRule //属性存取规则
...
对以上的解释可能还不太明白,不要着急,下面详细解释一下digester的基本原理,喝杯咖啡,慢慢来~
首先看看org.apache.commons.digester.Digester这个类,查看source发现Digester本身继承了DefaultHandler句柄,DefaultHandler句柄是SAX中基于时间驱动的缺省的句柄实现(包含ContentHandler, ErrorHandler, EntityResolver, DTDHandler),这个句柄不用多介绍了吧,相信用过SAX的哥们都明白。:)。刚刚不是说到了Rule了嘛,digester中定义了一个规则处理接口org.apache.commons.digester.Rule,此接口类似于ContentHandler接口中的方法,稍稍有点不同,主要有begin(), body(), end(), finish()方法。而digester缺省定义了许多有效的常用规则,每个规则都实现这个接口, 如果没有什么特殊需求,一般这些规则是够用了,罗列一下:BeanPropertySetterRule, CallMethodRule, CallParamRule, FactoryCreateRule,
NodeCreateRule, ObjectCreateRule, ObjectParamRule,PathCallParamRule,
SetNestedPropertiesRule, SetNextRule, SetPropertiesRule, SetPropertyRule,
SetRootRule,
SetTopRule,这些规则的意思稍后说。同时,对这些规则,digester还定义了一个规则的容器接口Rules(抽象类),这个抽象类接口容器容纳规则,并定义了规则匹配的模式,digester实现了一个基本的匹配模式RulesBase,简要看看这个实现中的两个最重要的方法:
.....
public void add(String pattern, Rule rule) {
// to help users who accidently add '/' to the end of their patterns
int patternLength = pattern.length();
if (patternLength>1 && pattern.endsWith("/") {
pattern = pattern.substring(0, patternLength-1);
}
List list = (List) cache.get(pattern);
if (list == null) {
list = new ArrayList();
cache.put(pattern, list);
}
list.add(rule);
rules.add(rule);
if (this.digester != null) {
rule.setDigester(this.digester);
}
if (this.namespaceURI != null) {
rule.setNamespaceURI(this.namespaceURI);
}
}
public List match(String namespaceURI, String pattern) {
// List rulesList = (List) this.cache.get(pattern);
List rulesList = lookup(namespaceURI, pattern);
if ((rulesList == null) || (rulesList.size() < 1)) {
// Find the longest key, ie more discriminant
String longKey = "";
Iterator keys = this.cache.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if (key.startsWith("*/") {
if (pattern.equals(key.substring(2)) ||
pattern.endsWith(key.substring(1))) {
if (key.length() > longKey.length()) {
// rulesList = (List) this.cache.get(key);
rulesList = lookup(namespaceURI, key);
longKey = key;
}
}
}
}
}
if (rulesList == null) {
rulesList = new ArrayList();
}
return (rulesList);
}
...
以上基本实现只是digester默认匹配规则,如果你要更换自己的规则匹配模式,则只需要继承org.apache.commons.digester.Rules接口,定义自己的匹配方式,digester同时还给我们提供了一个比较复杂,不过非常常用的匹配模式,那就是通配符匹配模式,引入了”!“、”*“、”?“三个符号进行通配的匹配模式,这个类就是org.apache.commons.digester.ExtendsBaseRules,后续再说。
digester就是通过以上的几种接口组件,同时配合操作数栈,进行XML解析。具体说,就是在parse XML文档之前,预先向容器集合(默认就是RulesBase容器)对XML文档中的节点path注入匹配规则,然后在parse文档的时候,遭遇到节点时时,调用SAX句柄中相应的方法,配合操作数栈,根据定义好的匹配模式,调用相应规则中的方法,将XML序列化成Java Object。介绍有点抽象,沿用digester本身带的例子介绍一下:
...
//对如下的XML文档
<address-book>
<person id="1" category="acquaintance">
<name>Gonzo</name>
<email type="business">gonzo@muppets.com</email>
</person>
<person id="2" category="rolemodel">
<name>Kermit</name>
<email type="business">kermit@muppets.com</email>
<email type="home">kermie@acme.com</email>
</person>
</address-book>
Digester digester = new Digester();
AddressBook book = new AddressBook();
d.push(book); //将AddressBook实例压入堆栈
digester.addObjectCreate("address-book/person", Person.class);//对person节点注入对象创建规则,即在SAX的事件遭遇到person节点的时候,创建Person类的实例,并压入堆栈,此时堆栈中从栈顶到栈底分别为AddressBook实例,Person类实例。
digester.addSetProperties("address-book/person");//对person节点注入属性设置规则,即在SAX的事件遭遇到person节点中的Attributes时,根据属性列表中的属性值对,这儿就是id="1", category="acquaintance",使用Java反射(reflection)机制,调用当前栈顶对象即Person实例类中id、category属性的标准的JavaBean方法,setId, setCategory。
digester.addSetNext("address-book/person", "addPerson");//对person节点注入父节点方法调用规则,即在SAX事件遭遇到person节点的时候,调用栈中Person实例的父实例中的addPerson方法。d.addCallMethod("address-book/person/name", "setName", 0);//对name节点注入方法调用规则,调用当前栈顶对象即Person实例中的setName方法,而此方法的参数即是当前name节点的字符内容。通常这个规则和addCallParam规则配合使用,这儿是一种特殊情况。
digester.addCallMethod("address-book/person/email", "addEmail", 2);//对email节点注入方法调用规则,调用当前栈顶对象即Person实例中的addEmail方法,此方法需要两个参数,一个是从属性值的type属性获取,一个是从email本身的字符内容获取。
digester.addCallParam("address-book/person/email", 0, "type");//对email节点注入参数调用规则,将当前节点的type属性值压入方法操作数栈
digester.addCallParam("address-book/person/email", 1);//对email节点注入参数调用规则,将当前节点的字符属性值压入方法操作数栈。
System.out.println(book);//打印book中值。。
...
通过以上注释应该不难理解吧。
下面再对以上所说的几种常用规则作一个详细的介绍:
ObjectCreateRule:这个规则比较简单,此规则就是对指定的模式创建一个类的实例,并将当前实例压入堆栈,并且在遭遇元素结束
时,将当前的栈顶实例弹出栈。
对应Digester中有关这个规则的Javadoc方法说明:
addObjectCreate(java.lang.String pattern, java.lang.Class clazz) - 方法参数说明了一切
addObjectCreate(java.lang.String pattern, java.lang.String className) - 同上
addObjectCreate(java.lang.String pattern, java.lang.String attributeName, java.lang.Class clazz) - 这个稍微解释一下,
多了一个参数attributeName,这个参数的意思就是如果在当前匹配模式的节点中定义了属性,则默认就采用这个attributeName所
对应的值来加载实例。比如以上面的例子加入person元素还有一个属性, class="test.org.apache.commons.digester.Person1",
则此规则会加载Person1实例而不是Person实例。明白?!
addObjectCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeN
ame) -同上
FactoryCreateRule:这个规则是基于工厂模式创建指定模式的一个类的实例。跟ObjectCreateRule类似,不同的是其参数Class继承了
ObjectCreationFactory接口。此接口中有个方法createObject(Attributes attrs),创建类的实例,在规则中将此类的实例压入堆栈。
对应Digester中有关这个规则的Javadoc方法说明:
addFactoryCreate(java.lang.String pattern, java.lang.Class clazz) - 一目了然,不用说了。只是clazz必须是实现了
ObjectCreationFactory接口的类。
addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, boolean ignoreCreateExceptions) - 同上,多了一个参
数,ignoreCreateExceptions表明是否忽略在创建类的过程中忽略抛出的exception。
addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, java.lang.String attributeName) - 稍微介绍一下这里
的attributeName参数,这个参数跟ObjectCreationRule规则中的attributeName雷同,不同的是这个属性的值必须是实现了接口
ObjectCreationFactory接口的类。
addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, java.lang.String attributeName, boolean ignoreCreateExceptions)
- 同上
addFactoryCreate(java.lang.String pattern, ObjectCreationFactory creationFactory) - 同上
addFactoryCreate(java.lang.String pattern, ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) - 同上
addFactoryCreate(java.lang.String pattern, java.lang.String className) - 同上
addFactoryCreate(java.lang.String pattern, java.lang.String className, boolean ignoreCreateExceptions) - 同上
addFactoryCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName) - 同上
addFactoryCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName, boolean ignoreCreateExceptions)
- 同上
BeanPropertySetterRule:Bean属性设置规则,对匹配当前指定模式的元素设置bean属性同名或者指定属性的值。
对应Digester中有关这个规则的Javadoc方法说明:
addBeanPropertySetter(java.lang.String pattern) - 对匹配当前指定模式的元素设置bean属性同名属性的值。
addBeanPropertySetter(java.lang.String pattern, java.lang.String propertyName) - 对匹配当前指定模式的元素设置bean属性指定属性的值。
CallMethodRule:方法调用规则,对匹配当前指定模式的元素,初始化指定的方法类型和方法参数,并压入堆栈。此规则需要和CallMethodRule规则配合使用:
对应Digester中有关这个规则的Javadoc方法说明:
addCallMethod(java.lang.String pattern, java.lang.String methodName) - 初始化指定的方法,只是当前方法不需要任何参数。
addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount) - 初始化指定的方法,参数个数为
paramCount,参数类型缺省为java.lang.String。注意有种特殊情况,就是paramCount为0的时候,默认使用当前元素的字符数据作为参数值。
addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount, java.lang.Class[] paramTypes) -
同上,只是有指定的参数类型.
addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount, java.lang.String[] paramTypes) -
同上
CallParamRule:提供CallMethodRule规则所需要的参数,必须跟CallMethodRule配合使用。
对应Digester中有关这个规则的Javadoc方法说明:
addCallParam(java.lang.String pattern, int paramIndex) - 使用匹配当前指定模式元素的字符数据作为索引为paramIndex的参数值.
addCallParam(java.lang.String pattern, int paramIndex, boolean fromStack) - 从操作数栈中,默认取出栈顶对象作为索引为paramIndex的参数值.
addCallParam(java.lang.String pattern, int paramIndex, int stackIndex) - 从操作数栈中,取出从栈顶数第stackIndex + 1个对象作为索引为paramIndex的参数值.
addCallParam(java.lang.String pattern, int paramIndex, java.lang.String attributeName) - 使用属性attrbuteName的值作为索引为paramIndex的参数值.
PathCallParamRule:提供当前匹配的模式路径作为方法调用所需要的参数,配合CallMethodRule使用。
对应Digester中有关这个规则的Javadoc方法说明:
addCallParamPath(java.lang.String pattern, int paramIndex) - 指定索引为paramIndex的值为当前匹配模式的路径.
ObjectParamRule;指定对象作为指定索引的值,配合CallMethodRule使用。
对应Digester中有关这个规则的Javadoc方法说明:
addObjectParam(java.lang.String pattern, int paramIndex, java.lang.Object paramObj) - 指定索引为paramIndex的值为给定的对象的值.
SetNestedPropertiesRule:当前匹配模式的直接子元素和对应bean的属性之间的映射.
对应Digester中有关这个规则的Javadoc方法说明:
addSetNestedProperties(java.lang.String pattern) - 默认当前匹配模式的元素的直接子元素和bean中对应属性之间值的映射
addSetNestedProperties(java.lang.String pattern, java.lang.String[] elementNames, java.lang.String[] propertyNames) -
当前匹配模式的直接子元素集和bean中属性集之间的映射
addSetNestedProperties(java.lang.String pattern, java.lang.String elementName, java.lang.String propertyName) -
当前匹配模式的直接子元素和bean中属性之间的映射
SetNextRule:匹配当前模式时,将栈顶对象作为次栈顶对象中指定方法的参数。
对应Digester中有关这个规则的Javadoc方法说明:
addSetNext(java.lang.String pattern, java.lang.String methodName) - 指定次栈顶元素的方法名称,将栈顶对象作为指定方法的参数。
addSetNext(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) - 指定次栈顶元素的方法名称和参数类型,将栈顶对象作为指定方法的参数。
SetPropertiesRule:匹配当前模式的元素的属性与栈顶对象中同名或者指定对应关系的属性值。
对应Digester中有关这个规则的Javadoc方法说明:
addSetProperties(java.lang.String pattern) - 指定栈顶对象属性的值为当前匹配元素中同名元素属性的值。
addSetProperties(java.lang.String pattern, java.lang.String[] attributeNames, java.lang.String[] propertyNames) - 对应栈顶对象属性的值为指定的当前匹配元素中元素属性的值
addSetProperties(java.lang.String pattern, java.lang.String attributeName, java.lang.String propertyName) - 同上
SetPropertyRule:不常用的一个规则,主要用于key-value值对。一个元素属性为栈顶对象的属性,一个元素属性为栈顶对象的属性的值。
对应Digester中有关这个规则的Javadoc方法说明:
addSetProperty(java.lang.String pattern, java.lang.String name, java.lang.String value) - name和value都是匹配当前模式的元素属性,name的值是栈顶对象中同名的属性名称,而value的值是栈顶对象中属性名称为name的值。(好像有点绕口)
SetRootRule:将当前栈顶对象作为根对象中指定方法的参数。
对应Digester中有关这个规则的Javadoc方法说明:
addSetRoot(java.lang.String pattern, java.lang.String methodName) - 将当前栈顶对象作为根对象中指定为methodName方法的参数。
addSetRoot(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) - 同上,只是多了一个方法参数的类型
SetTopRule:与SetNextRule正好想法,是将次栈顶元素作为栈顶元素指定方法的参数。
对应Digester中有关这个规则的Javadoc方法说明:
addSetTop(java.lang.String pattern, java.lang.String methodName) - 将次栈顶元素作为栈顶元素指定为methodName方法的参数。
addSetTop(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) - 同上,只是多了一个方法参数的类型
前面介绍的基本就是digester的常用主要用法,正常来说足够了!不过为了提供一些额外更强大的扩展,digester提供了扩展的通配符匹配规则,更强大也更方便!那就是ExtendedBaseRules,这个类扩展了基本的匹配规则RulesBase,提供了更通用的通配符匹配规则,以下简要介绍一下:
首先说一下基本的匹配模式,有三种:
Parent Match(可以理解为匹配子元素的精确父匹配):a/b/c/? */a/b/c/?
Ancester Match(可以理解为匹配那种出身自一个精确序列元素的元素):a/b/* */a/b/*
Universal Wildcard Match(可以理解为通配符匹配,都以!开头):!*a/b !a/b/? !*a/b/? !a/b/* !*/a/b/*
Wild Match (可以理解为更通用更模糊的通配符匹配):* !*
?代表直接子元素
* 代表任意的父或子元素
! 代表以什么什么为开头
举个例子:
Digester digester = new Digester();
digester.setRules(new ExtendedBaseRules());
digester.setValidating(false);
digester.addObjectCreate("!*/b", BetaBean.class);
digester.addObjectCreate("!*/a", AlphaBean.class);
digester.addObjectCreate("root", ArrayList.class);
digester.addSetProperties("!*");
digester.addSetNext("!*/b/?", "setChild");
digester.addSetNext("!*/a/?", "setChild");
digester.addSetNext("!root/?", "add");
ArrayList root =
(ArrayList) digester.parse(getInputStream("Test4.xml"));
assertEquals("Wrong array size", 2, root.size());
AlphaBean one = (AlphaBean) root.get(0);
assertTrue(one.getChild() instanceof BetaBean);
BetaBean two = (BetaBean) one.getChild();
assertEquals("Wrong name (1)", two.getName() , "TWO");
assertTrue(two.getChild() instanceof AlphaBean);
AlphaBean three = (AlphaBean) two.getChild();
assertEquals("Wrong name (2)", three.getName() , "THREE");
BetaBean four = (BetaBean) root.get(1);
assertEquals("Wrong name (3)", four.getName() , "FOUR");
assertTrue(four.getChild() instanceof BetaBean);
BetaBean five = (BetaBean) four.getChild();
assertEquals("Wrong name (4)", five.getName() , "FIVE");
Test4.xml文件:
<root>
<a name="ONE">
<b name="TWO">
<a name="THREE"/>
</b>
</a>
<b name="FOUR">
<b name="FIVE"/>
</b>
</root>
分享到:
相关推荐
在本案例中,我们将深入探讨`digester`的用法,以及如何通过它来实现XML到Java对象的映射。 首先,我们需要了解XML解析的基本概念。XML(eXtensible Markup Language)是一种用于标记数据的语言,常用于存储和传输...
Digester使用堆栈来保存xml节点(stack.push()方法),当该xml节点中嵌套的所有子节点解析完毕,该节点将被弹出(stack.pup()方法)。Digester最大的优点就是使用模式匹配来表示xml文件中节点之间的父子关系 。
- **配置文件解析**:在很多框架中,如Spring,都使用类似Digester的方式解析XML配置文件,创建和配置对象。 - **对象的自动构建**:通过XML描述, Digester可以自动生成对象树,并按照XML结构设置对象属性,极大地...
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。 双语对照,边学技术、边学英语。
1. **XML到Java对象映射**:Digester允许开发者定义一系列的规则,这些规则将XML元素映射到Java对象的实例化、方法调用或属性设置。例如,一个`<user>`元素可能对应一个`User`类的实例,而`<user>`内的`<name>`元素...
下面我们将深入探讨 Digester 的关键概念和使用方法: 1. **初始化 Digester**: 在使用 Digester 之前,我们需要创建一个 Digester 实例,并设置一些基本配置,例如XML解析器的命名空间处理等。然后可以通过 `...
1. **设置消化规则**:定义XML元素与Java方法之间的映射规则,当解析到特定XML元素时, Digester会调用相应的Java方法。 2. **解析XML**:使用`Digester`解析XML文档,并根据设定的规则执行操作。 下面是一个基本的...
本文将深入探讨如何使用Apache的 Digester 库来解析XML文档,这是一款强大的工具,能够将XML数据映射到Java对象,简化了处理XML的过程。 Digester 是Apache Commons项目的一部分,它提供了一种规则驱动的方法来处理...
7. **XML解析与事件驱动**: Digester使用DOM(Document Object Model)解析XML文档,但它的核心是事件驱动的,即每当遇到匹配的XML元素时,它会触发相应的Rule。 8. **使用示例**: 在实际应用中,首先你需要创建一...
2. **初始化 Stack**:使用 `Digester.push()` 方法将一个初始对象放入 Digester 的 Stack 中。这个初始对象在整个解析过程中将起到桥梁的作用,使得最终可以访问到解析得到的对象。 3. **注册匹配模式和规则**:...
使用Digester的基本步骤如下: 1. 创建一个`Digester`实例。实例化后,它可以安全地重复使用,但需要注意的是,它是非线程安全的。 2. 配置`Digester`属性,例如设置是否验证XML文档。 3. 添加处理规则,这些规则...
3. **API变更**:如果你的代码是基于较新版本的`commons-digester`编写,那么使用旧版本的jar文件可能会导致方法调用不匹配。 4. **编译问题**:如果在Maven构建过程中出现了问题,例如网络不稳定导致部分依赖未下载...
3. **定义规则**:使用`addRule`或`addSetProperties`等方法定义规则,将XML元素与Java对象的方法或属性关联起来。例如,`addRule("xmlTag", new SomeRule())`会将XML文档中名为"xmlTag"的元素映射到一个自定义的`...
使用Digester的基本步骤包括创建Digester实例、设置解析规则、然后调用`parse()`方法解析XML。 4. **规则定义** Digester的规则是通过`addRule()`方法添加的。例如,你可以使用`addRuleSet()`添加一个预定义的...
- **初始化对象**: 使用`push`方法向Digester使用的栈中添加一个初始对象。这是为了确保在解析过程中能够正确地保存和访问创建的对象。 - **注册规则**: 为每个需要处理的XML元素注册对应的规则。 - **解析XML文件**...
赠送jar包:commons-digester3-3.2.jar;...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
在Java开发中,Apache Commons Digester是一个非常有用的库,它允许开发者通过XML配置来...在实际应用中,还可以结合使用这两种方法,以便在需要灵活性的地方使用硬编码规则,在其他地方利用规则文件来保持代码整洁。
在与digester结合使用时,它能帮助找到XML配置文件中引用的服务或者类,使得动态加载和配置成为可能。例如,当XML配置文件中包含类名但未提供完全限定路径时,commons-discovery可以自动查找并加载相应的类。 在...
本篇文章将深入剖析`Digester`的使用以及它在Tomcat7启动过程中的作用。 `Digester`是Apache Commons项目的一个组件,它的主要功能是解析XML文档,并根据预定义的规则创建和配置Java对象。在Tomcat的上下文中,这些...