1 概述
-
Digester是一款用于将xml转换为Java对象的事件驱动型工具,是对SAX的高层次的封装。Digester相对于SAX提供了更加友好的接口,隐藏了xml节点具体层次的细节,让开发者能加载专注于处理过程。
-
Digester最早是Web框架Apache Struts的一部分,后来由于其通用性移植到了Apache Common项目中。
2 Digester解决了SAX哪些问题
SAX基于事件解析Xml,最重要的就是要编写一个解析器类。
public class SaxHandler extends DefaultHandler {
/**
* 解析一个xml标签字符时触发回调
(该触发回调发生在startElement之后endElement之前)
* @param ch xml文档完整字符数组
* @param start 当前标签中字符在ch开始位置
* @param length 当前标签中字符的长度
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length){
..省略实现
}
/**
* 解析一个xml标签结束时触发回调
* @param uri
* @param localName
* @param name 当前标签的名称
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String name){
..省略实现
}
/**
* 解析一个xml标签开始时触发回调
* @param uri
* @param localName
* @param name 当前标签的名称
* @param attributes 标签中的属性对象
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
..省略实现
}
/**
* 解析一个xml文件开始时回调
*/
@Override
public void startDocument() throws SAXException{
..省略实现
}
/**
* 解析一个xml文件结束时回调
*/
@Override
public void endDocument() throws SAXException{
..省略实现
}
2.1 SAX缺陷
-
1 如果解析XML上不同标签节点存在依赖关系时,需要开发者自行维护依赖关系,说具体就是如果需要子标签对象手动设置到父标签属性中,子标签需要程序开发者手动保存下来。
-
2 不同xml的标签属性,字符都不同,而SAX只提供了所有标签通用回调事件,不能针对不同标签定制。
3 Digester设计
image-
1 Digester继承DefaultHandler表明Digester是对SAX的扩展实现
-
2 Digester内部存类型为Rules的属性,Rules为了一个其实现类为RulesBase,RulesBase内部维护HashMap,其中key对应匹配xml规则的字符串,value表示表示针对此xml规则解析规则列表。每个解析规则使用Rule类来表示。
public class Digester extends DefaultHandler2 {
...省略代码
protected Rules rules = null;
...省略代码
}
public class RulesBase implements Rules {
...省略代码
//String 表示匹配xml匹配规则(可以用正则表达式)
//List<Rule> 表示针对对应xml规则解析规则列表
protected HashMap<String,List<Rule>> cache = new HashMap<>();
...省略代码
- 3 同时Digester内部维护了一个栈数据结构,用来处理当前解析的xml标签节点,解析前会将xml标签对象入栈(push),解析后会将xml标签对象出栈(pop),栈最顶部的对象永远都是现在正在解析的对象。这样就可以将有父子关系的节点对象在栈中保存下来。
4 Rule规则对应接口方法
begin():当读取到匹配节点的开始部分时调用,会将该节点的所有属性作为从参数传入。
body():当读取到匹配节点的内容时调用,注意指的不是子节点,而是嵌入内容为普通文本。
end():当读取到匹配节点的结束部分时调用,如果存在子节点,只有当子节点处理完毕后该方法才会被调用。
finish():当整个parse()方法完成时调用,多用于清楚临时数据和缓存数据
可以发现这和DefaultHandler方式很像,不同的时规则只针对特定的某个xml标签规则
5 xml匹配规则
[图片上传失败...(image-4163f4-1565013353761)]
例子
<a>
<b>
<c></c>
</b>
<b>
<c></c>
<c></c>
<c></c>
</b>
</a>
a匹配<a>标签
a/b匹配<a><b>
a/b/c匹配<a><b><c>
*/b 匹配<a><b>
6 Digester常用API
public void setValidating(boolean validating) // 是否根据DTD校验XML
public void push(Object object) // 将对象压入栈
public Object peek() // 获取栈顶对象
public Object pop() // 弹出栈顶对象
public Object parse(InputSource input) // 解析输入源
public void addRule(String pattern, Rule rule) //针对指定xml标签设置规则解析器
7 给指定标签设置规则
1 创建一个定义规则类,该类需要继承Rule
2 调用digester.addRule API 函数
public class ConnectorCreateRule extends Rule {
@Override
public void begin(String namespace, String name, Attributes attributes){
//do something
}
@Override
public void begin(String namespace, String name, Attributes attributes){
//do something
}
}
//表示匹配到<Server><Service><Connector>结构的标签时,使用创建的自定义接口
digester.addRule("Server/Service/Connector",new ConnectorCreateRule());
为单个标签添加规则组(多个标签规则)
当需要某个xml规则添加多个规则时可以使用RuleSet
1 创建一个RuleSet实现类,该类需要继承RuleSetBase
2 构造方法中需要定义xml规则
3 实现addRuleInstances方法对传入的规则添加规则
public class MyRuleSet
extends RuleSetBase {
public MyRuleSet()
{
this("");
}
public MyRuleSet(String prefix)
{
super();
this.prefix = prefix;
this.namespaceURI = "http://www.mycompany.com/MyNamespace";
}
protected String prefix = null;
public void addRuleInstances(Digester digester)
{
digester.addObjectCreate( prefix + "foo/bar",
"com.mycompany.MyFoo" );
digester.addSetProperties( prefix + "foo/bar" );
}
}
digester.addRuleSet( new MyRuleSet( "baz/" ) );
内置的规则接口
Digester内置了一些规则,可以调用使用DigesterAPI 直接调用,给指定xml添加内置规则。
ObjectCreateRule: 当begin()方法调用时,该规则会将指定的Java类实例化,并将其放入对象栈。具体的Java类可由该规则在构造方法出啊如,也可以通过当前处理XML节点的某个属性指定,属性名称通过构造方法传入。当end()方法调用时,该规则创建的对象将从栈中取出。
FactoryCreateRule:ObJectCreateRule规则的一个变体,用于处理Java类无默认构造方法的情况,或者需要在Digester处理该对象之前执行某些操作的情况。
SetPropertiesRule:当begin()方法调用时,Digester使用标准的Java Bean属性操作方法(setter)将当前XML节点的属性值设置到对象栈顶部的对象中。
SetPropertyRule:当begin()方法调用时,Digester会设置栈顶部对象指定属性的值,其中属性名和属性值分别通过XML节点的两个属性指定。
SetNextRule:当end()方法调用时,Digester会找到位于栈顶部对象的下一个对象,并调用其指定的方法,同时将栈顶部对象作为参数传入,用于设置父对象的子对象,以便在栈对象之间建立父子关系,从而形成对象树,方便引用。
SetTopRule:与setNextRule对象,当end()方法调用时,Digester会找到位于站顶部的对象,调用其指定方法,同时将位于顶部下一个对象作为参数传入,用于设置当前对象的父对象。
CallMethRule:该规则用于在end()方法调用时执行栈顶对象的某个方法,参数值由CallParamRule获取。
CallParamRule:该规则与CallMethodRule配合使用,作为其子节点的处理规则创建方法参数,参数值可取自某个特殊属性,也可以取自节点的内容。
NodeCreateRule:用于将XML文档树的一部分转换为DOM节点,并放入栈。
DEMO
<?xml version="1.0" encoding="utf-8" ?>
<department name="deptname001" code="deptcode001">
<user name="username001" code="usercode001"></user>
<user name="username002" code="usercode002"></user>
<extension>
<property-name>director</property-name>
<property-value>joke</property-value>
</extension>
</department>
package com.wuhao.web.tomcat.digester;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Department {
private String name;
private String code;
private Map<String, String> extension = new HashMap<String, String>();
private List<User> users = new ArrayList<User>();
public void addUser(User user){
this.users.add(user);
}
public void putExtension(String name,String value){
this.extension.put(name,value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Map<String, String> getExtension() {
return extension;
}
public void setExtension(Map<String, String> extension) {
this.extension = extension;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
package com.wuhao.web.tomcat.digester;
public class User {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
package com.wuhao.web.tomcat.digester;
import org.apache.tomcat.util.digester.Digester;
import java.io.File;
import java.net.URL;
public class DigesterRule {
public Department execute(String filePath) throws Exception {
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
// addObjectCreate方法的意思是碰到xml文件中的department节点则创建一个Department对象
digester.addObjectCreate("department", "com.wuhao.web.tomcat.digester.Department");
// addSetProperties方法的意思是根据department节点中的属性信息调用相应属性的setter方法
digester.addSetProperties("department");
// addObjectCreate方法的意思是碰到xml文件中的department节点则创建一个Department对象
digester.addObjectCreate("department/user", "com.wuhao.web.tomcat.digester.User");
digester.addSetProperties("department/user");
digester.addSetNext("department/user", "addUser", "com.wuhao.web.tomcat.digester.User");
digester.addCallMethod("department/extension", "putExtension", 2);
digester.addCallParam("department/extension/property-name", 0);
digester.addCallParam("department/extension/property-value", 1);
URL url = this.getClass().getClassLoader().getResource(filePath);
return (Department) digester.parse(new File(url.getFile()));
}
}
public class Test {
@org.junit.Test
public void testJavaRule() throws Exception {
Department department = new DigesterRule().execute("tomcat/department.xml");
System.out.println(department);
}
}
网友评论