`
dmh920
  • 浏览: 18620 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类
最新评论

浅析Digester

    博客分类:
  • JAVA
阅读更多

         最近在看tomcat源代码,发现tomcat的xml文件都是通过Digester解析自动生成了对象,于是对Digester研究了下,为了防止自己忘记便记录下来。首先通过一个小程序来了解Digester,解析下面的school.xml文件:

<?xml version="1.0" encoding="UTF-8"?>  
<school name="xx大学" address="xx省xx市">  
    <department description="计算机系">  
        <student name="小明" sex="男"/>  
        <student name="小红" sex="女"/>  
        <student name="小王" sex="男"/>  
    </department>  
    <department description="数学系">  
        <student name="小李" sex="男"/>  
        <student name="小新" sex="男"/>  
        <student name="小赵" sex="女"/>  
    </department>  
</school>
public class DigesterForSchool {  
    public static void main(String[] args) throws Exception{  
        String path = System.getProperty("user.dir") + File.separator + "etc";  
        Digester digester = new Digester();  
        digester.addObjectCreate("school", "com.test.digester.School");  
        digester.addSetProperties("school");  
        digester.addObjectCreate("school/department", "com.test.digester.Department");  
        digester.addSetProperties("school/department");  
        digester.addSetNext("school/department", "addDepartment");  
        digester.addObjectCreate("school/department/student",  
                "com.test.digester.Student");  
        digester.addSetNext("school/department/student", "addStudent");  
        digester.addSetProperties("school/department/student");  
        School school = (School)digester.parse(new File(path, "school.xml"));  
        System.out.println(school);  
    }  
}

输出结果如下:

school name: xx大学, address: xx省xx市

departments: [

department description: 计算机系

students: [

student name: 小明 sex: 男, 

student name: 小红 sex: 女, 

student name: 小王 sex: 男], 

department description: 数学系

students: [

student name: 小李 sex: 男, 

student name: 小新 sex: 男, 

student name: 小赵 sex: 女]]

        可以发现Digester将school.xml文件的内容,转换为了相对应的对象。那Digester到底是怎样工作的呢?查看Digester源代码我们发现:

import org.xml.sax.XMLReader;  
public class Digester extends DefaultHandler

        原来Digester是SAX解析器事件处理类DefaultHandler的子类,我们知道SAX是基于事件驱动解析xml文件,当SAX扫描到文档(document)开始、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。所以Digester在事件处理函数中做了相应的处理,让其能按照自己的规则生成对象数据。现在来看看Digester在事件处理函数中做了哪些处理:

public void startElement( String namespaceURI, String localName, String qName, Attributes list ) throws SAXException {
	...
	List<Rule> rules = getRules().match( namespaceURI, match, localName, list );
        matches.push( rules );
	...
	for ( int i = 0; i < rules.size(); i++ ) {
		...
		Rule rule = rules.get(i);
                rule.begin( namespaceURI, name, list );
		...	
	}
}
public void characters( char buffer[], int start, int length ) throws SAXException {
	...
	bodyText.append( buffer, start, length );
}
public void endElement( String namespaceURI, String localName, String qName ) throws SAXException {
	...
	List<Rule> rules = matches.pop();
	...
        for ( int i = 0; i < rules.size(); i++ ) {
                ....
                Rule rule = rules.get( i );
                ....
                rule.body( namespaceURI, name, bodyText );
        }
        ...
        for ( int i = 0; i < rules.size(); i++ ) {
                ....
                Rule rule = rules.get( i );
                ....
                rule.end( namespaceURI, name, bodyText );
        }
        ....
}

         在执行startElement()方法和endElement()方法的时候都用到Rule类的相关方法,现在再看看api中对Rule类的描述:Concrete implementations of this class implement actions to be taken when a corresponding nested pattern of XML elements has been matched. 当匹配到xml某个模式时,Rule的具体实现类的动作将被执行。在解析xml文档时,当Digester实例匹配到某个模式的元素开始标签时,调用相对应的Rule对象的begin方法(见startElement()方法),当匹配到相应元素的结束标签时,调用Rule对象的body()和end()方法(见endElement()方法)。当Digester类可以包含0个或多个Rule对象。在Digester实例中,这些规则和其相关联的模式都存储在Rules接口表示的一类集合中。每当把一条规则添加到Digester实例中时,Rule对象也都会被添加到Rules对象中。当调用addObjectCreate()、addCallMethod()方法、addSetNext()方法或其他方法时,都会间接调用Digester类的addRule()方法。该方法会将一个Rule对象和它所匹配的模式添加到Digester对象的Rules集合中。 

public void addObjectCreate( String pattern, String className ) {   
        addRule( pattern, new ObjectCreateRule( className ) );  
}  
public void addRule( String pattern, Rule rule ) {  
        rule.setDigester( this );  
        getRules().add( pattern, rule );  
}  
public Rules getRules() {  
        if ( this.rules == null ) {  
            this.rules = new RulesBase();  
            this.rules.setDigester( this );  
        }  
        return ( this.rules );  
}

      Digester已经预定义了一些规则,可以直接使用这些规则,如果这些规则仍然不满足需求,可以实现自己的规则,常用的预定义规则如下:

1.创建对象:

        ObjectCreateRule:利用指定类的默认构造函数。创建该类的一个对象,并把对象压入栈,当元素处理结束时,对象被弹出。
        FactoryCreateRule:利用指定的工厂类创建一个对象,用于处理没有提供默认构造函数的类。注意的是用于该规则的工厂类必须实现org.apache.commons.digester.ObjectCreationFactory接口。
2.设置属性:
        SetPropertiesRule:利用指定名称的XML元素属性值,设置顶层Bean的属性。
        BeanPropertySetterRule:把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。(通常用来处理<element>10</element>之类的结构)。
        SetPropertyRule:设置顶层Bean的一个属性。无论是Bean属性的名称,还是赋予该属性的值,都在当前XML元素中以属性的形式指定,例如:<student key="age" value="10" />。
3.对象之间关系:
        SetNextRule:peek出栈顶对象和栈顶下一个对象,将栈顶对象作为参数传递给栈顶下一个元素。
        SetTopRule:把栈顶下一个对象传递给顶层对象。
        SetRootRule:调用栈底对象的一个方法,并把栈顶的对象作为参数传入。
4.调用方法:
        CallMethodRule:调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数,参数的值通过后继的CallParamRule给出。
        CallParamRule:表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性,或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。

      Digester实例内部维护着一个用于临时存储创建对象的栈和一个临时存储参数的栈(这个栈主要是用于CallMethodRule和CallParamRule使用,可以自行查阅源代码),如果实现自己的Rule时使用到了Digester中的栈,在begin()方法中,将一个Object push进去,一定要记得在end()方法中pop出来。Rule的api也说道:If a rule wishes to manipulate a digester stack (the default object stack, a named stack, or the parameter stack) then it should only ever push objects in the rule's begin method and always pop exactly the same number of objects off the stack during the rule's end method. Of course peeking at the objects on the stacks can be done from anywhere. 那现在我们来实现一个自定义规则,将上面的xml文件的student元素修改下命名为school2.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<school name="xx大学" address="xx省xx市">  
    <department id="1" description="计算机系">  
        <student name="小明" sex="男" birthday="1990-01-01 00:00:00"/>  
        <student name="小红" sex="女" birthday="1991-01-01 00:00:00"/>  
        <student name="小王" sex="男" birthday="1992-01-01 00:00:00"/>  
    </department>  
    <department id="2" description="数学系">  
        <student name="小李" sex="男" birthday="1990-01-02 00:00:00"/>  
        <student name="小新" sex="男" birthday="1991-01-02 00:00:00"/>  
        <student name="小赵" sex="女" birthday="1992-01-02 00:00:00"/>  
    </department>  
</school>

        student元素新增了一个属性"birthday",在Student类中新增了一个成员变量Date型的birthday,这时在给birthday属性赋值时就不能直接调用addSetProperties()方法了,因为从xml解析到的birthday是string类型的数据,如果直接调用addSetProperties()方法会抛出类型转换异常,所以写了一个新的设置属性规则和一个将字符串日期类型转换为Date型数据的规则,代码如下:

public class SetAllPropertiesRule extends Rule {
	protected Map<String,String> excludes = new HashMap<String,String>();
    
	public SetAllPropertiesRule() {}

	/**
	 * 构造一个SetAllPropertiesRule
	 * @param exclude 不需要设置属性值的属性名数组
	 */
	public SetAllPropertiesRule(String[] exclude) {
             for (int i=0; i<exclude.length; i++ ) {
        	if (exclude[i]!=null) { 
        		this.excludes.put(exclude[i],exclude[i]);
        	}
             }
        }
	
	@Override
	public void begin(String namespace, String name, Attributes attributes)
			throws Exception {
		Map<String, String> values = new HashMap<String, String>();
		for (int i = 0; i < attributes.getLength(); i++) {
			String attributeName = attributes.getLocalName(i);
                        if ("".equals(attributeName)) {
                            attributeName = attributes.getQName(i);
                        }
                        String value = attributes.getValue(i);
                        if (!excludes.containsKey(attributeName)){
            	            values.put(attributeName, value );
                        }
		}
		populate(getDigester().peek(), values);
	}
}
public class ConvertStringToDateRule extends Rule {
	private SimpleDateFormat format = null;;
	
	private String attributeName = null;
	
	/**
	 * 构造一个ConvertStringToDateRule
	 * @param pattern 需要转换的字符串日期格式
	 */
	public ConvertStringToDateRule(String pattern) {
		this(pattern, null);
	}
	
	/**
	 * 构造一个ConvertStringToDateRule
	 * @param pattern 需要转换的字符串日期格式
	 * @param attributeName 需要转换的元素属性名,如果为null则将元素间的字符串转换,
         * 如:</element>2012-01-01 00:00:00</element>
	 */
	public ConvertStringToDateRule(String pattern, String attributeName) {
		this.attributeName = attributeName;
		format = new SimpleDateFormat(pattern);
	}
	
	@Override
	public void begin(String namespace, String name, Attributes attributes)
			throws Exception {
		if(attributeName == null) {
			return;
		}
		String str = attributes.getValue(attributeName);
		Date date = format.parse(str); 
		getDigester().push(date);
	}
	
	@Override
	public void body(String namespace, String name, String text)
			throws Exception {
		if(attributeName == null) {
			Date date = format.parse(text);
			getDigester().push(date);
		}
	}
	
	@Override
	public void end(String namespace, String name) throws Exception {
		getDigester().pop();
	}
}
public class DigesterForSchool2 {
	public static void main(String[] args) throws Exception {
		String path = System.getProperty("user.dir") + File.separator + "etc";
		Digester digester = new Digester();
		digester.addObjectCreate("school", "com.test.digester.school2.School");
		digester.addSetProperties("school");
		digester.addObjectCreate("school/department",
				"com.test.digester.school2.Department");
		digester.addSetProperties("school/department");
		digester.addSetNext("school/department", "addDepartment");
		digester.addObjectCreate("school/department/student",
				"com.test.digester.school2.Student");
		digester.addSetNext("school/department/student", "addStudent");
		digester.addRule("school/department/student", new SetAllPropertiesRule(
				new String[] { "birthday" }));
		digester.addRule("school/department/student",
				new ConvertStringToDateRule("yyyy-MM-dd hh:mm:ss", "birthday"));
		digester.addSetNext("school/department/student", "setBirthday");
		School school = (School) digester.parse(new File(path, "school2.xml"));
		System.out.println(school);
	}
} 

输出结果如下:

school name: xx大学 address: xx省xx市

departments: [

department id: 1 description: 计算机系

students: [

student name: 小明 sex: 男 birthday: Mon Jan 01 00:00:00 CST 1990, 

student name: 小红 sex: 女 birthday: Tue Jan 01 00:00:00 CST 1991, 

student name: 小王 sex: 男 birthday: Wed Jan 01 00:00:00 CST 1992], 

department id: 2 description: 数学系

students: [

student name: 小李 sex: 男 birthday: Tue Jan 02 00:00:00 CST 1990, 

student name: 小新 sex: 男 birthday: Wed Jan 02 00:00:00 CST 1991, 

student name: 小赵 sex: 女 birthday: Thu Jan 02 00:00:00 CST 1992]]

 

 

分享到:
评论

相关推荐

    Digester

    Digester是Apache软件基金会下的Jakarta项目中的一个Java库,主要用于简化XML到Java对象的映射过程。在处理XML文档时,它通过匹配XML元素结构到相应的Java对象的方法调用,实现了XML解析的自动化。这个工具对于那些...

    commons-digester3-3.2-API文档-中英对照版.zip

    赠送jar包:commons-digester3-3.2.jar; 赠送原API文档:commons-digester3-3.2-javadoc.jar; 赠送源代码:commons-digester3-3.2-sources.jar; 赠送Maven依赖信息文件:commons-digester3-3.2.pom; 包含翻译后...

    digester用法测试案例

    Digester是Apache软件基金会的Jakarta项目中的一个实用工具库,它主要用来解析XML文档,并根据预定义的规则自动创建和配置Java对象。这个工具在处理XML到Java对象映射时,极大地简化了代码,避免了手动解析XML的繁琐...

    Castor、digester实例

    Castor和Digester是两个在Java开发中用于对象与XML数据之间进行映射的库,它们简化了XML数据的解析和对象的序列化过程。在本文中,我们将深入探讨这两个库,以及如何通过实例来使用它们。 首先,让我们了解Castor。...

    commons-digester.jar

    `commons-digester.jar`是Apache Commons项目中的一个组件,主要功能是解析XML文档并根据预定义的规则自动创建和填充Java对象。这个组件在Java应用程序中尤其有用,特别是那些需要从XML配置文件中构建复杂对象层次...

    Digester java解析xml

    Java中的Digester库是Apache Commons项目的一部分,它提供了一种方便的方式来解析XML文档,并将解析结果映射到Java对象模型上。这个库特别适合于创建简单的XML到Java对象的映射,而不需要编写大量的手动解析代码。在...

    commons-digester-2.1.jar

    `commons-digester-2.1.jar` 是Apache Commons项目中的一个组件,主要负责XML文档的解析和对象的创建与绑定。Apache Commons Digester库提供了一种规则驱动的方法来解析XML文档,并根据预定义的规则将XML数据映射到...

    digester使用

    《digester深度解析》 Java世界中,XML作为数据交换和配置文件的常用格式,其解析和对象绑定是一项常见的任务。Apache Commons Digester是Apache软件基金会提供的一个强大的工具,专门用于将XML文档解析为Java对象...

    利用commons-digester解析xml

    标题“利用commons-digester解析XML”涉及到的是Java开发中的一种处理XML文档的工具——Apache Commons Digester。这个库提供了一种方便的方式来映射XML文档结构到Java对象,从而简化了XML数据的处理过程。 Apache ...

    Digester读取xml教程.rar

    《使用Digester解析XML的深度指南》 在Java开发中,处理XML文件是常见的任务,而Apache Commons Digester库提供了一种高效且便捷的方式来解析XML并将其映射到Java对象。本教程将深入探讨如何使用Digester来读取XML...

    Digester两种解析方式

    在Java开发中,Apache Commons Digester是一个非常有用的库,它允许开发者通过XML配置来实例化、配置和关联Java对象。 Digester提供了两种主要的解析方式,即规则硬编码和独立的规则文件,这两种方法各有其特点和...

    digester组件简化了xml文件处理操作

    ### Digester组件简化XML文件处理操作 #### 一、引言 随着Web技术的发展,XML作为数据交换格式的重要性日益凸显。然而,对于大型且复杂的XML文档进行解析与处理时,传统的方法如DOM(Document Object Model)和SAX...

    XML的解析之——使用Digester

    本文将深入探讨如何使用Apache的 Digester 库来解析XML文档,这是一款强大的工具,能够将XML数据映射到Java对象,简化了处理XML的过程。 Digester 是Apache Commons项目的一部分,它提供了一种规则驱动的方法来处理...

    commons-digester3-3.2-API文档-中文版.zip

    赠送jar包:commons-digester3-3.2.jar; 赠送原API文档:commons-digester3-3.2-javadoc.jar; 赠送源代码:commons-digester3-3.2-sources.jar; 赠送Maven依赖信息文件:commons-digester3-3.2.pom; 包含翻译后...

    digester3.2 源码

    《digester3.2源码解析与应用实例》 Apache Digester是一个强大的Java库,用于在XML文档和Java对象之间建立映射关系,通过规则来自动解析XML并创建或更新对象结构。在digester3.2版本中,我们能够深入理解其内部...

    Digester解析XML的小例子(对象嵌套)

    在Java开发中,Struts框架提供了一个强大的工具——Digester,用于解析XML文件并自动创建、配置Java对象。本文将详细介绍如何使用Digester处理具有嵌套结构的XML文档,并通过一个具体的实例——"DigesterXmlTest"来...

    使用Apache_Commons_Digester

    ### 使用Apache Commons Digester开发指南 #### 概述 Apache Commons Digester 是一款基于 Java 的开源库,专门用于简化 XML 文件解析的过程。它利用 SAX(Simple API for XML)解析器来解析 XML 数据,并通过一...

    digester解析xml的问题.pdf

    Digester 是 Apache Commons 中的一个工具类库,它用于解析 XML 文档,并根据预先定义的规则自动创建和配置 Java 对象。在上述问题中,我们看到一个 XML 文档表示了一个考试,其中包含了多个题目,每个题目有其编号...

Global site tag (gtag.js) - Google Analytics