【XML系列】详解Digester处理XML(一)

Digester框架属于Jakarta Commons,它以规则和模式为基础处理XML文档。与SAXDOM之类的标准API相比,Digester不涉及太多的细节问题,非常适合于对XML文档进行简单的处理。

  在JavaXML开发中,一个常见的任务是把XML文档转换成对应的Java Bean对象的层次结构。人们经常用标准的SAX和DOM  API来完成这个任务。虽然这两种API都很强大和灵活,但对于某些简单的任务来说,它们显得操作层次太低,也就是说,涉及了太多的细节问题。Jakarta Digester框架能够很好地满足这类场合的需要。     

Digester框架简介

    

    JakartaDigester框架从Struts框架发展而来,原先被用来处理struts-config.xml配置文件,但很快人们认识到它有着更广泛的用途,把它转入了Jakarta Commons项目。Jakarta   Commons的目标是提供一个“可重用Java组件的仓库”。Digester最新的版本是1.3,于2002年8月13日发布。

    Digester框架允许开发者指定一组动作,当解析器在XML文档中发现某些特定的简单模式时动作被执行。Digester框架带有10个预定义的规则(Rule),涵盖了unmarshalling XML(例如创建Bean或设置Bean属性)的大多数需求,但必要时用户可以定义和实现自己的规则。

marshalling的原意是指“配制整齐,编组列车”,marshalling是在内存中为Java对象生成XML描述文档的过程,

unmarshalling是指把XML形式的描述转换到可用Java代码操作的对象的过程,我们称之为“反配制”

  

在本文的例子中,我们将反配制下面这个XML文档:     

<?xml version="1.0"?> 
<catalog library="somewhere"> 
  <book> 
    <author>Author 1</author> 
    <title>Title 1</title> 
  </book>
  <book> 
    <author>Author 2</author>
    <title>His One Book</title> 
  </book> 
  <magazine> 
    <name>Mag Title 1</name> 
    <article page="5"> 
      <headline>Some Headline</headline> 
    </article> 
    <article page="9"> 
      <headline>Another Headline</headline> 
    </article> 
  </magazine> 
  <book> 
    <author>Author 2</author> 
    <title>His Other Book</title> 
  </book> 
  <magazine> 
    <name>Mag Title 2</name> 
    <article page="17"> 
      <headline>Second Headline</headline> 
    </article> 
  </magazine> 
</catalog>

  

下面是Bean的代码。注意使用Digester框架时,Bean类必须定义成public。 

public class Article {
	private String headline;
	private String page;
	public Article() {

	}
	public void setHeadline(String rhs) {
		headline = rhs;
	}
	public void setPage(String rhs) {
		page = rhs;
	}
	public String toString() {
		return "Article:   Headline='" + headline + "'   on   page='" + page + "'   ";
	}
}

public class Magazine {
	private String name;
	private Vector articles;

	public Magazine() {
		articles = new Vector();
	}

	public void setName(String rhs) {
		name = rhs;
	}

	public void addArticle(Article a) {
		articles.addElement(a);
	}

	public String toString() {
		StringBuffer buf = new StringBuffer("Magazine:   Name='" + name + "'   ");
		for (int i = 0; i < articles.size(); i++) {
			buf.append(articles.elementAt(i).toString());
		}
		return buf.toString();
	}
}

public class Book {
	private String author;
	private String title;
	public Book() {

	}
	public void setAuthor(String rhs) {
		author = rhs;
	}
	public void setTitle(String rhs) {
		title = rhs;
	}
	public String toString() {
		return "Book:   Author='" + author + "'   Title='" + title + "'";
	}

}

public class Catalog {
	private Vector books;
	private Vector magazines;
	public Catalog() {
		books = new Vector();
		magazines = new Vector();
	}
	public void addBook(Book rhs) {
		books.addElement(rhs);
	}
	public void addMagazine(Magazine rhs) {
		magazines.addElement(rhs);
	}
	public String toString() {
		String newline = System.getProperty("line.separator");
		StringBuffer buf = new StringBuffer();
		buf.append("---   Books   ---").append(newline);
		for (int i = 0; i < books.size(); i++) {
			buf.append(books.elementAt(i)).append(newline);
		}
		buf.append("---   Magazines   ---").append(newline);
		for (int i = 0; i < magazines.size(); i++) {
			buf.append(magazines.elementAt(i)).append(newline);
		}
		return buf.toString();
	}

}


指定模式和规则 

    

    Digester框架以模式(Pattern)和规则(Rule)为基础处理输入的XML。模式必须与XML元素匹配,包括其名字和在文档树内的位置。描述匹配模式的语法类似于XPath匹配模式,例如:catalog模式匹配顶层的<catalog>元素,catalog/book模式匹配直接嵌套在<catalog>元素内的<book>元素(但不匹配文档内其他位置的<book>元素)。     

    所有的模式都必须指定其完整名称——从根元素开始的完整路径。

唯一的例外是包含通配符(“*”)的模式,例如*/name模式匹配XML文档内任何位置的<name>元素。但是根元素不必特别指出,因为所有的路径都是从根元素开始的绝对路径。

  当Digester发现一个指定的模式,它就执行关联的任务。由此可见,Digester框架显然与SAX解析器有着密切的关系(实际上,Digester类实现了org.xml.sax.ContentHandler,并维护着解析栈)。所有在Digester中使用的规则必须扩展org.apache.commons.digester.Rule,后者本身提供了一些类似于SAXContentHandler回调函数的方法。例如,当遇到匹配元素的开始标记和结束标记时,begin()方法和end()方法将分别被调用。     

    一旦遇到匹配元素的内容,body()方法被调用;最后被调用的方法是finish(),这个方法在匹配元素的结束标记处理完毕之后被调用,用来执行可能需要的事后清理任务。然而,大多数时候我们不必关注这些方法,因为框架提供的标准规则很可能已经提供了所有必需的功能。

    要反配制一个文档,首先创建一个org.apache.commons.digester.Digester类的实例,如果必要的话,进行一些配置操作,指定必需的模式和规则,最后向parse()方法传递一个XML文件的引用。下面的DigesterDriver示范了这一处理过程(必须在命令行上指定输入XM`L文档的名称)。 

public class DigesterDriver {
	public static void main(String[] args) {
		try {
			Digester digester = new Digester();
			digester.setValidating(false);
			digester.addObjectCreate("catalog", Catalog.class);
			// xml中的<catalog>对应上类Catalog
			digester.addObjectCreate("catalog/book", Book.class);
			// xml中的<catalog><book>对应上类Book
			digester.addBeanPropertySetter("catalog/book/author", "author");
			// xml中的<catalog><book><author>对应上类Book的author
			// <author>Author 2</author>
			digester.addBeanPropertySetter("catalog/book/title", "title");
			// xml中的<catalog><book><title>对应上类Book的title
			digester.addSetNext("catalog/book", "addBook");
			// xml中的<catalog></book>对应Catalog.addBook(),将生成的Book对象加入
			digester.addObjectCreate("catalog/magazine", Magazine.class);
			// xml中的<catalog><magazine>对应类Magazine
			digester.addBeanPropertySetter("catalog/magazine/name", "name"); // xml中的<catalog><magazine><name>对应类Magazine的name
			digester.addObjectCreate("catalog/magazine/article", Article.class);
			// xml中的<catalog><magazine><article>对应类article
			digester.addSetProperties("catalog/magazine/article", "page", "page");
			// xml中的<catalog><magazine><article page>对应类article的page
			digester.addBeanPropertySetter("catalog/magazine/article/headline");
			digester.addSetNext("catalog/magazine/article", "addArticle");
			digester.addSetNext("catalog/magazine", "addMagazine");
			File input = new File(args[0]);
			Catalog c = (Catalog) digester.parse(input);
			System.out.println(c.toString());
		} catch (Exception exc) {
			exc.printStackTrace();

		}
	}
}


在上面的代码中,我们首先创建了Digester类的一个实例digester

digester.setValidating(false);

然后指定它不要用DTD验证XML文档的合法性——这是因为我们没有为XML文档定义DTD接下来,我们指定了模式和关联的规则:ObjectCreateRule创建指定类的一个实例,并将它压入解析栈。

       digester.addObjectCreate("catalog", Catalog.class);

SetPropertiesRuleBean属性设置成当前XML元素的属性值——规则的第一个参数是XML属性的名称,第二个参数是Bean属性的名称。SetPropertiesRule获取的是XML属性的值

        digester.addSetProperties("catalog/magazine/article", "page", "page");

或者digester.addSetProperties("database/user");它缺省将user下的属性装入名字相同的私有属性中。

BeanPropertySetterRule获取的是位于当前元素内的原始字符数据值。使用BeanPropertySetterRule时不必指定要设置的Bean属性名字,默认是当前XML元素的名称。

digester.addBeanPropertySetter("catalog/magazine/vx""name");

在上面的例子中,在匹配catalog/magazine/article/headline模式的规则定义中使用的就是默认值。

    digester.addBeanPropertySetter("catalog/magazine/article/headline");

SetNextRule弹出解析栈顶部的对象,并把该对象传递给它下面对象的指定名称的方法——通常用来把一个配置完毕的Bean插入父对象。

digester.addSetNext("catalog/book""addBook");

注意,我们可以为同一个模式注册多个规则。如果注册了多个规则,则这些规则按照它们被加入到Digester的次序执行,例如,如果要处理catalog/magazine/article的元素,我们首先创建合适的article Bean,然后设置page属性,最后弹出完成后的article Bean,并把它插入magazine。 

    

调用任意方法

    

     我们不仅可以设置Bean的属性,而且还可以调用堆栈内对象的任意方法。这通过CallMethodRule完成,我们只需指定方法名字,如有必要,再说明调用的参数类型和数量。CallParamRule用来定义传递给被调用函数的参数值,参数值可以从当前XML元素的命名的属性获取,也可以从当前元素包含的原始字符数据获取。例如,在前面实现DigesterDriver的例子中,我们可以不用BeanPropertySetterRule,而是通过显式调用属性的set方法达到同样的目的:【setter】

    digester.addCallMethod("catalog/book/author""setAuthor", 1);

    digester.addCallParam("catalog/book/author", 0); 

<author>tt</author>tt作为参数并调用setAuthor方法。(可以设置多个参数)

  上面的第一行代码给出了要调用的方法(即setAuthor()),以及该调用需要的参数数量(即1)。第二行代码的意思是从元素包含的字符数据获取函数参数的值,把它作为参数数组的第一个传入(即索引是0的数组元素)。

【getter】如果我们指定了XML元素属性的名称,

(例如digester.addCallParam("catalog/book/author", 0, "author");),则参数值将从当前元素的相应属性值获取。

这里必须注意的是,

digester.addCallMethod("pattern""methodName");

这个语句不是指定了一个不带参数的方法调用,而是指定了带有一个参数的方法调用,它的值就是当前XML元素的字符数据!这样,我们又有了另一种替代BeanPropertySetterRule的办法:

digester.addCallMethod("catalog/book/author","setAuthor",0);

    <author>tt</author>tt作为参数并调用setAuthor方法。

   如果要调用一个确实没有参数的方法,必须采用如下形式:

digester.addCallMethod("pattern","methodName");。     

标准规则概要

  下面简要说明所有标准规则。

1创建 

ObjectCreateRule:利用指定类的默认构造函数,创建该类的一个对象,并把对象压入栈。当元素处理结束时,对象被弹出。被实例化的类可通过class对象或类的全称给出。     

FactoryCreateRule:利用指定的工厂类创建一个对象,把对象压入栈。对于没有提供默认构造函数的类,这一规则很有用。用于该规则的工厂类必须实现org.apache.commons.digester.ObjectCreationFactory接口。     

2设置属性

SetPropertiesRule:利用指定名称的XML元素属性值,设置顶层Bean的一个或者多个指定名称的属性。XML元素的属性名称和Bean的属性名称以String[]数组形式传入该规则(通常用来处理之类的结构)。

BeanPropertySetterRule:把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。(通常用来处理<page>10</page>之类的结构)。

SetPropertyRule:设置顶层Bean的一个属性。无论是Bean属性的名称,还是赋予该属性的值,都在当前XML元素中以属性的形式指定,

例如:<article key="page" value="10" />

3管理父/子关系

SetNextRule:弹出栈顶的对象,把它传递给紧接其下的另一个对象的指定名称的方法。通常用来把一个已经初始化的Bean插入到父对象。 

SetTopRule:把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setParenet方法时,这一规则很有用。 

SetRootRule:调用栈底对象的一个方法,并把栈顶的对象作为参数传入。 

4调用任意方法

CallMethodRule:调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数,参数的值通过后继的CallParamRule给出。     

CallParamRule:表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性,或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。


你可能感兴趣的:(【XML系列】详解Digester处理XML(一))