org.apache.commons.digester.Digester使用

 

commons.digester软件包学习

   http://www.onjava.com/pub/a/onjava/2002/10/23/digester.html?page=1

它能方便地将XML文档所定义的元素转化为JAVA对象,其实它的用法有点象栈(当然内在的原理就是那个古老的东西,只是提供了更高一层的封装)
      //
生成一个digester。主要需要引进commons-logging.jarcommons-collections.jarcommons-       beanutils.jar
          Digester digester = new Digester();
         
          //
设置对XML文档资料是否进行DTD验证

          digester.setValidating( false );
         
          //
当遇见 catalog 元素的时候,产生一个Catalog对象
          digester.addObjectCreate( "catalog", Catalog.class );
         
          //
当遇见 catalog 元素下面的book的时候,产生一个Book对象
          digester.addObjectCreate( "catalog/book", Book.class );
          //
当遇见 catalog 元素下面的bookauthor时候,调用author属性的Set方法
          digester.addBeanPropertySetter( "catalog/book/author", "author" );
          digester.addBeanPropertySetter( "catalog/book/title", "title" );
          //
当再一次遇见 catalog 元素下面的book的时候,调用catalog类的addBook()方法
          digester.addSetNext( "catalog/book", "addBook" );

          digester.addObjectCreate( "catalog/magazine", Magazine.class );
          digester.addBeanPropertySetter( "catalog/magazine/name", "name" );

          digester.addObjectCreate( "catalog/magazine/article", Article.class );
          //addSetProperties
()是将对应元素的属性赋值。
          digester.addSetProperties( "catalog/magazine/article", "page", "page" );
          digester.addBeanPropertySetter( "catalog/magazine/article/headline" );
          digester.addSetNext( "catalog/magazine/article", "addArticle" );

          digester.addSetNext( "catalog/magazine", "addMagazine" );
          //"F://Digester//catalog.xml"
XML文档
          File input = new File( "F://Digester//catalog.xml" );
          Catalog c = (Catalog)digester.parse( input );
          System.out.println( c.toString() );
在测试以上网站的例子的时候引进:commons-digester.jar,另外请你引进:commons-logging.jarcommons-collections.jarcommons-beanutils.jar
这是因为在执行: Digester digester = new Digester();的时候配置了LOG(在apache下面的很多组件都会用到它的公用组件)。

 

 

Learning and Using Jakarta Digester

by Philipp K. Janert, Ph.D.
10/23/2002

Turning an XML document into a corresponding hierarchy of Java bean objects is a fairly common task. In a previous article, I described how to accomplish this using the standard SAX and DOM APIs.

<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript> </script>

Although powerful and flexible, both APIs are, in effect, too low-level for the specific task at hand. Furthermore, the unmarshalling procedure itself requires a fair amount of coding: a parse-stack must be maintained when using SAX, and the DOM-tree must be navigated when using DOM.

This is where the Apache Jakarta Commons Digester framework comes in.

The Jakarta Digester Framework

The Jakarta Digester framework grew out of the Jakarta Struts Web toolkit. Originally developed to process the central struts-config.xml configuration file, it was soon recognized that the framework was more generally useful, and moved to the Jakarta Commons project, the stated goal of which is to provide a "repository of reusable Java components." The most recent version, Digester 1.3, was released on August 13, 2002.

The Digester class lets the application programmer specify a set of actions to be performed whenever the parser encounters certain simple patterns in the XML document. The Digester framework comes with 10 prepackaged "rules," which cover most of the required tasks when unmarshalling XML (such as creating a bean or setting a bean property), but each user is free to define and implement his or her own rules, as necessary.

The Example Document and Beans

In this example, we will unmarshall the same XML document that we used in the previous article:

<?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>

The bean classes are also the same, except for one important change: In the previous article, I had declared these classes to have package scope -- primarily so that I could define all of them in the same source file! Using the Digester framework, this is no longer possible; the classes need to be declared as public (as is required for classes conforming to the JavaBeans specification):

import java.util.Vector;

 

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();

   }

}


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 + "'";

   }

}


import java.util.Vector;

 

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 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 + "' ";

   }

}

import org.apache.commons.digester.*;
       

   
     
   
import java.io.*;
       
import java.util.*;
       

   
     
   
public class DigesterDriver {
       

   
     
   
   public static void main( String[] args ) {
       

   
     
   
      try {
       
         Digester digester = new Digester();
       
         digester.setValidating( false );
       

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

   
     
   
         digester.addObjectCreate( "catalog/book", Book.class );
       
         digester.addBeanPropertySetter( "catalog/book/author", "author" );
       
         digester.addBeanPropertySetter( "catalog/book/title", "title" );
       
         digester.addSetNext( "catalog/book", "addBook" );
       

   
     
   
         digester.addObjectCreate( "catalog/magazine", Magazine.class );
       
         digester.addBeanPropertySetter( "catalog/magazine/name", "name" );
       

   
     
   
         digester.addObjectCreate( "catalog/magazine/article", Article.class );
       
         digester.addSetProperties( "catalog/magazine/article", "page", "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();
       
      }
       
   }
       
}

After instantiating the Digester, we specify that it should not validate the XML document against a DTD -- because we did not define one for our simple Catalog document. Then we specify the patterns and the associated rules: the ObjectCreateRule creates an instance of the specified class and pushes it onto the parse stack. The SetPropertiesRule sets a bean property to the value of an XML attribute of the current element -- the first argument to the rule is the name of the attribute, the second, the name of the property.

Whereas SetPropertiesRule takes the value from an attribute, BeanPropertySetterRule takes the value from the raw character data nested inside of the current element. It is not necessary to specify the name of the property to set when using BeanPropertySetterRule: it defaults to the name of the current XML element. In the example above, this default is being used in the rule definition matching the catalog/magazine/article/headline pattern. Finally, the SetNextRule pops the object on top of the parse stack and passes it to the named method on the object below it -- it is commonly used to insert a finished bean into its parent.

Note that it is possible to register several rules for the same pattern. If this occurs, the rules are executed in the order in which they are added to the Digester -- for instance, to deal with the <article> element, found at catalog/magazine/article, we first create the appropriate article bean, then set the page property, and finally pop the completed article bean and insert it into its magazine parent.

Invoking Arbitrary Functions

It is not only possible to set bean properties, but to invoke arbitrary methods on objects in the stack. This is accomplished using the CallMethodRule to specify the method name and, optionally, the number and type of arguments passed to it. Subsequent specifications of the CallParamRule define the parameter values to be passed to the invoked functions. The values can be taken either from named attributes of the current XML element, or from the raw character data contained by the current element. For instance, rather than using the BeanPropertySetterRule in the DigesterDriver implementation above, we could have achieved the same effect by calling the property setter explicitly, and passing the data as parameter:

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

The first line gives the name of the method to call (setAuthor()), and the expected number of parameters (1). The second line says to take the value of the function parameter from the character data contained in the <author> element and pass it as first element in the array of arguments (i.e., the array element with index 0). Had we also specified an attribute name (e.g., digester.addCallParam( "catalog/book/author", 0, "author" );), the value would have been taken from the respective attribute of the current element instead.

One important caveat: confusingly, digester.addCallMethod( "pattern", "methodName", 0 ); does not specify a call to a method taking no arguments -- instead, it specifies a call to a method taking one argument, the value of which is taken from the character data of the current XML element! We therefore have yet another way to implement a replacement for BeanPropertySetterRule:

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

To call a method that truly takes no parameters, use digester.addCallMethod( "pattern", "methodName" );.

Creational

·         ObjectCreateRule: Creates an object of the specified class using its default constructor and pushes it onto the stack; it is popped when the element completes. The class to instantiate can be given through a class object or the fully-qualified class name.

·         FactoryCreateRule: Creates an object using a specified factory class and pushes it onto the stack. This can be useful for classes that do not provide a default constructor. The factory class must implement the org.apache.commons.digester.ObjectCreationFactory interface.

Property Setters

·         SetPropertiesRule: Sets one or several named properties in the top-level bean using the values of named XML element attributes. Attribute names and property names are passed to this rule in String[] arrays. (Typically used to handle XML constructs like <article page="10">.)

·         BeanPropertySetterRule: Sets a named property on the top-level bean to the character data enclosed by the current XML element. (Example: <page>10</page>.)

·         SetPropertyRule: Sets a property on the top-level bean. Both the property name, as well as the value to which this property will be set, are given as attributes to the current XML element. (Example: <article key="page" value="10" />.)

Parent/Child Management

·         SetNextRule: Pops the object on top of the stack and passes it to a named method on the object immediately below. Typically used to insert a completed bean into its parent.

·         SetTopRule: Passes the second-to-top object on the stack to the top-level object. This is useful if the child object exposes a setParent method, rather than the other way around.

·         SetRootRule: Calls a method on the object at the bottom of the stack, passing the object on top of the stack as argument.

Arbitrary Method Calls

·         CallMethodRule: Calls an arbitrary named method on the top-level bean. The method may take an arbitrary set of parameters. The values of the parameters are given by subsequent applications of the CallParamRule.

·         CallParamRule: Represents the value of a method parameter. The value of the parameter is either taken from a named XML element attribute, or from the raw character data enclosed by the current element. This rule requires that its position on the parameter list is specified by an integer index.

So far, we have specified the patterns and rules programmatically at compile time. While conceptually simple and straightforward, this feels a bit odd: the entire framework is about recognizing and handling structure and data at run time, but here we go fixing the behavior at compile time! Large numbers of fixed strings in source code typically indicate that something is being configured (rather than programmed), which could be (and probably should be) done at run time instead.

The org.apache.commons.digester.xmlrules package addresses this issue. It provides the DigesterLoader class, which reads the pattern/rule-pairs from an XML document and returns a digester already configured accordingly. The XML document configuring the Digester must comply with the digester-rules.dtd, which is part of the xmlrules package.

Below is the contents of the configuration file (named rules.xml) for the example application. I want to point out several things here.

Patterns can be specified in two different ways: either as attributes to each XML element representing a rule, or using the <pattern> element. The pattern defined by the latter is valid for all contained rule elements. Both ways can be mixed, and <pattern> elements can be nested -- in either case, the pattern defined by the child element is appended to the pattern defined in the enclosing <pattern> element.

The <alias> element is used with the <set-properties-rule> to map an XML attribute to a bean property.

Finally, using the current release of the Digester package, it is not possible to specify the BeanPropertySetterRule in the configuration file. Instead, we are using the CallMethodRule to achieve the same effect, as explained above.

<?xml version="1.0"?>

 

<digester-rules>

   <object-create-rule pattern="catalog" classname="Catalog" />

   <set-properties-rule pattern="catalog" >

      <alias attr-name="library" prop-name="library" />

   </set-properties-rule>

 

   <pattern value="catalog/book">

      <object-create-rule classname="Book" />

      <call-method-rule pattern="author" methodname="setAuthor"

                        paramcount="0" />

      <call-method-rule pattern="title" methodname="setTitle"

                        paramcount="0" />

      <set-next-rule methodname="addBook" />

   </pattern>

 

   <pattern value="catalog/magazine">

      <object-create-rule classname="Magazine" />

 

      <call-method-rule pattern="name" methodname="setName" paramcount="0" />

 

      <pattern value="article">

         <object-create-rule classname="Article" />

         <set-properties-rule>

            <alias attr-name="page" prop-name="page" />

         </set-properties-rule>   

         <call-method-rule pattern="headline" methodname="setHeadline"

                          paramcount="0" />

         <set-next-rule methodname="addArticle" />

      </pattern>

 

      <set-next-rule methodname="addMagazine" />

   </pattern>

</digester-rules>

Since all the actual work has now been delegated to the Digester and DigesterLoader classes, the driver class itself becomes trivially simple. To run it, specify the catalog document as the first command line argument, and the rules.xml file as the second. (Confusingly, the DigesterLoader will not read the rules.xml file from a File or an org.xml.sax.InputSource, but requires a URL -- the File reference in the code below is therefore transformed into an equivalent URL.)

import org.apache.commons.digester.*;

import org.apache.commons.digester.xmlrules.*;

 

import java.io.*;

import java.util.*;

 

public class XmlRulesDriver {

   public static void main( String[] args ) {

      try {

 

         File input = new File( args[0] );

         File rules = new File( args[1] );

 

         Digester digester = DigesterLoader.createDigester( rules.toURL() );

 

         Catalog catalog = (Catalog)digester.parse( input );

         System.out.println( catalog.toString() );

 

      } catch( Exception exc ) {

         exc.printStackTrace();

      }

   }

}

Conclusion

This concludes our brief overview of the Jakarta Commons Digester package. Of course, there is more. One topic ignored in this introduction are XML namespaces: Digester allows you to specify rules that only act on elements defined within a certain namespace.

We mentioned briefly the possibility of developing custom rules, by extending the Rule class. The Digester class exposes the customary push(), peek(), and pop() methods, giving the individual developer freedom to manipulate the parse stack directly.

Lastly, note that there is an additional package providing a Digester implementation which deals with RSS (Rich-Site-Summary)-formatted newsfeeds. The Javadoc tells the full story.

References

Philipp K. Janert, Ph.D. is a software project consultant, server programmer, and architect.


 

<think>好的,用户想了解Apache Commons Digester中的Rule类。我需要先回忆一下相关知识,确保信息准确。 首先,Commons Digester是一个用于处理XML到Java对象映射的库。Rule类在其中扮演重要角色,用于定义解析XML时的行为。 我应该先介绍Rule的基本概念,说明它的作用是在解析XML时触发特定操作。然后需要详细说明Rule的生命周期方法,比如begin(), body(), end(),以及可能的参数处理。这些方法对应XML元素的不同解析阶段,用户可能想知道每个方法的具体用途和调用时机。 接下来,需要提到如何与Digester配合使用,比如通过addRule()或addRuleSet()来添加规则。可能还需要说明自定义Rule的步骤,让用户了解如何扩展功能。 另外,常见的内置Rule子类如BeanPropertySetter、CallMethodRule等,这些例子能帮助用户理解具体的应用场景。最后,强调Rule的优势,比如解耦和可扩展性,以及适用场景,比如配置文件解析或对象构建。 需要确保语言简洁,结构清晰,分点说明,避免技术术语过多,同时覆盖用户可能关心的方面。检查是否有过时的信息,确保所有内容符合最新版本的情况。可能用户是想解决XML解析的问题,或者希望自定义解析行为,因此重点放在使用方法和实际应用上。</think>Apache Commons Digester的`Rule`类是Digester库的核心组件之一,主要用于定义XML解析过程中如何将XML元素映射到Java对象。以下从原理、使用和场景三个层面进行说明: ### 1. Rule 的核心作用 `Rule`类定义了XML元素与Java对象之间的映射规则。当Digester解析XML文档时,会根据预定义的`Rule`在特定节点触发对应的操作。例如: - 创建Java对象实例 - 设置对象属性 - 调用指定方法 - 建立对象间的层级关系 ### 2. Rule 的生命周期方法 每个`Rule`子类通过重写以下关键方法实现逻辑: ```java // 当匹配到XML元素开始时触发 public void begin(String namespace, String name, Attributes attributes) {...} // 处理元素内的文本内容 public void body(String namespace, String name, String text) {...} // 当匹配到XML元素结束时触发 public void end(String namespace, String name) {...} ``` ### 3. 典型使用流程 ```java Digester digester = new Digester(); digester.addRule("library/book", new Rule() { @Override public void begin(Attributes attributes) { Book book = new Book(); getDigester().push(book); // 将对象压入栈 } @Override public void end() { Book book = (Book) getDigester().pop(); Library lib = (Library) getDigester().peek(); lib.addBook(book); } }); ``` ### 4. 常用内置Rule子类 | 规则类 | 功能说明 | 示例用法 | |----------------------|---------------------------------|-------------------------------------| | `ObjectCreateRule` | 创建指定类的实例 | `digester.addObjectCreate("path", Book.class)` | | `SetPropertiesRule` | 设置对象属性(基于XML属性) | `digester.addSetProperties("path")` | | `BeanPropertySetter` | 通过setter方法设置属性 | `digester.addBeanPropertySetter("path", "propertyName")` | | `CallMethodRule` | 调用指定方法 | `digester.addCallMethod("path", "methodName", paramCount)` | ### 5. 自定义Rule示例 处理复杂属性设置: ```java public class DateFormatRule extends Rule { @Override public void body(String namespace, String name, String text) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { Date date = sdf.parse(text); getDigester().peek().setPublicationDate(date); } catch (ParseException e) { throw new RuntimeException("日期格式错误"); } } } ``` ### 6. 适用场景 - **配置文件解析**:将XML格式的配置转换为Java配置对象 - **对象树构建**:处理具有复杂层次结构的XML文档 - **数据绑定**:将XML数据自动填充到POJO中 - **协议处理**:解析特定格式的XML通信协议 ### 7. 性能优化建议 - **复用Rule实例**:避免重复创建相同Rule对象 - **控制匹配粒度**:使用精确的XPath表达式 - **合理使用栈操作**:通过`push()`/`pop()`管理对象层级 - **预编译规则集**:通过`RuleSet`接口组织相关规则 > **注意**:虽然Digester仍被广泛使用,但对于新项目建议考虑JAXB或Jackson XML等更现代的解决方案。Digester 3.x版本已停止维护,推荐使用兼容的Fork版本(如commons-digester3)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值