用Digester简化XML文档处理

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

     
  Digester框架简介    
   
        Jakarta的Digester框架从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文档:    

  1. xml   version="1.0"?>      
  2.   <catalog   library="somewhere">      
  3.       <book>      
  4.           <author>Author   1author>      
  5.           <title>Title   1title>      
  6.       book>      
  7.       <book>      
  8.           <author>Author   2author>      
  9.           <title>His   One   Booktitle>      
  10.       book>      
  11.       <magazine>      
  12.           <name>Mag   Title   1name>      
  13.           <article   page="5">      
  14.               <headline>Some   Headlineheadline>      
  15.           article>      
  16.           <article   page="9">      
  17.               <headline>Another   Headlineheadline>      
  18.           article>      
  19.       magazine>      
  20.       <book>      
  21.           <author>Author   2author>      
  22.           <title>His   Other   Booktitle>      
  23.       book>      
  24.       <magazine>      
  25.           <name>Mag   Title   2name>      
  26.           <article   page="17">      
  27.               <headline>Second   Headlineheadline>      
  28.           article>      
  29.       magazine>      
  30.   catalog>      
  31.       


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

  1. import   java.util.Vector;      
  2.   public   class   Catalog   {      
  3.       private   Vector   books;      
  4.       private   Vector   magazines;      
  5.       public   Catalog()   {      
  6.           books   =   new   Vector();      
  7.           magazines   =   new   Vector();      
  8.       }      
  9.       public   void   addBook(   Book   rhs   )   {      
  10.           books.addElement(   rhs   );      
  11.       }      
  12.       public   void   addMagazine(   Magazine   rhs   )   {      
  13.           magazines.addElement(   rhs   );      
  14.       }      
  15.       public   String   toString()   {      
  16.           String   newline   =   System.getProperty(   "line.separator"   );      
  17.           StringBuffer   buf   =   new   StringBuffer();      
  18.           buf.append(   "---   Books   ---"   ).append(   newline   );      
  19.           for(   int   i=0;   i
  20.               buf.append(   books.elementAt(i)   ).append(   newline   );      
  21.           }      
  22.           buf.append(   "---   Magazines   ---"   ).append(   newline   );      
  23.           for(   int   i=0;   i
  24.               buf.append(   magazines.elementAt(i)   ).append(   newline   );      
  25.           }      
  26.           return   buf.toString();      
  27.       }      
  28.   }      
  29.   //===================================================      
  30.   public   class   Book   {      
  31.       private   String   author;      
  32.       private   String   title;      
  33.       public   Book()   {}      
  34.       public   void   setAuthor(   String   rhs   )   {   author   =   rhs;   }      
  35.       public   void   setTitle(   String   rhs   )   {   title   =   rhs;   }      
  36.       public   String   toString()   {      
  37.           return   "Book:   Author='"   +   author   +   "'   Title='"   +   title   +   "'";      
  38.       }      
  39.   }      
  40.   //===================================================      
  41.   import   java.util.Vector;      
  42.   public   class   Magazine   {      
  43.       private   String   name;      
  44.       private   Vector   articles;      
  45.       public   Magazine()   {      
  46.           articles   =   new   Vector();      
  47.       }      
  48.       public   void   setName(   String   rhs   )   {   name   =   rhs;   }      
  49.       public   void   addArticle(   Article   a   )   {      
  50.           articles.addElement(   a   );      
  51.       }      
  52.       public   String   toString()   {      
  53.           StringBuffer   buf   =   new   StringBuffer(   "Magazine:   Name='"   +   name   +   "'   ");      
  54.           for(   int   i=0;   i
  55.               buf.append(   articles.elementAt(i).toString()   );      
  56.           }      
  57.           return   buf.toString();      
  58.       }      
  59.   }      
  60.   //===================================================      
  61.   public   class   Article   {      
  62.       private   String   headline;      
  63.       private   String   page;      
  64.       public   Article()   {}      
  65.       public   void   setHeadline(   String   rhs   )   {   headline   =   rhs;   }      
  66.       public   void   setPage(   String   rhs   )   {   page   =   rhs;   }      
  67.       public   String   toString()   {      
  68.           return   "Article:   Headline='"   +   headline   +   "'   on   page='"   +   page   +   "'   ";      
  69.       }      
  70.   }      


  指定模式和规则    
   
        Digester框架以模式(Pattern)和规则(Rule)为基础处理输入的XML。模式必须与XML元素匹配,包括其名字和在文档树内的位置。描述匹配模式的语法类似于XPath匹配模式,例如:catalog模式匹配顶层的<catalog></catalog>元素,catalog/book模式匹配直接嵌套在<catalog></catalog>元素内的<book></book>元素(但不匹配文档内其他位置的<book></book>元素)。    
  所有的模式都必须指定其完整名称——从根元素开始的完整路径。唯一的例外是包含通配符(“*”)的模式,例如*/name模式匹配XML文档内任何位置的<name></name>元素。但是根元素不必特别指出,因为所有的路径都是从根元素开始的绝对路径。     
         当Digester发现一个指定的模式,它就执行关联的任务。由此可见,Digester框架显然与SAX解析器有着密切的关系(实际上,Digester类实现了org.xml.sax.ContentHandler,并维护着解析栈)。所有在Digester中使用的规则必须扩展org.apache.commons.digester.Rule,后者本身提供了一些类似于SAX的ContentHandler回调函数的方法。例如,当遇到匹配元素的开始标记和结束标记时,begin()方法和end()方法将分别被调用。    
  一旦遇到匹配元素的内容,body()方法被调用;最后被调用的方法是finish(),这个方法在匹配元素的结束标记处理完毕之后被调用,用来执行可能需要的事后清理任务。然而,大多数时候我们不必关注这些方法,因为框架提供的标准规则很可能已经提供了所有必需的功能。    
  要反配制一个文档,首先创建一个org.apache.commons.digester.Digester类的实例,如果必要的话,进行一些配置操作,指定必需的模式和规则,最后向parse()方法传递一个XML文件的引用。下面的DigesterDriver示范了这一处理过程(必须在命令行上指定输入XML文档的名称)。      

  1. import   org.apache.commons.digester.*;      
  2.   import   java.io.*;      
  3.   import   java.util.*;      
  4.   public   class   DigesterDriver   {      
  5.       public   static   void   main(   String[]   args   )   {      
  6.           try   {      
  7.               Digester   digester   =   new   Digester();      
  8.               digester.setValidating(   false   );      
  9.               digester.addObjectCreate(   "catalog",   Catalog.class   );      
  10.               digester.addObjectCreate(   "catalog/book",   Book.class   );      
  11.               digester.addBeanPropertySetter(   "catalog/book/author",   "author"   );      
  12.               digester.addBeanPropertySetter(   "catalog/book/title",   "title"   );      
  13.               digester.addSetNext(   "catalog/book",   "addBook"   );      
  14.               digester.addObjectCreate(   "catalog/magazine",   Magazine.class   );      
  15.               digester.addBeanPropertySetter(   "catalog/magazine/name",   "name"   );      
  16.               digester.addObjectCreate(   "catalog/magazine/article",   Article.class   );      
  17.               digester.addSetProperties(   "catalog/magazine/article",   "page",   "page"   );      
  18.               digester.addBeanPropertySetter(   "catalog/magazine/article/headline"   );        
  19.               digester.addSetNext(   "catalog/magazine/article",   "addArticle"   );      
  20.               digester.addSetNext(   "catalog/magazine",   "addMagazine"   );      
  21.               File   input   =   new   File(   args[0]   );      
  22.               Catalog   c   =   (Catalog)digester.parse(   input   );      
  23.               System.out.println(   c.toString()   );      
  24.           }   catch(   Exception   exc   )   {      
  25.               exc.printStackTrace();      
  26.           }      
  27.       }      
  28.   }      
  29.       


       在上面的代码中,我们首先创建了Digester类的一个实例digester,然后指定它不要用DTD验证XML文档的合法性——这是因为我们没有为XML文档定义DTD。接下来,我们指定了模式和关联的规则:ObjectCreateRule创建指定类的一个实例,并将它压入解析栈。SetPropertiesRule把Bean属性设置成当前XML元素的属性值——规则的第一个参数是XML属性的名称,第二个参数是Bean属性的名称。    
        SetPropertiesRule获取的是XML属性的值,而BeanPropertySetterRule获取的是位于当前元素内的原始字符数据值。使用BeanPropertySetterRule时不必指定要设置的Bean属性名字,默认是当前XML元素的名称。在上面的例子中,在匹配catalog/magazine/article/headline模式的规则定义中使用的就是默认值。最后,SetNextRule弹出解析栈顶部的对象,并把该对象传递给它下面对象的指定名称的方法——通常用来把一个配置完毕的Bean插入父对象。    
  注意,我们可以为同一个模式注册多个规则。如果注册了多个规则,则这些规则按照它们被加入到Digester的次序执行,例如,如果要处理catalog/magazine/article的元素,我们首先创建合适的article   Bean,然后设置page属性,最后弹出完成后的article   Bean,并把它插入magazine。    
   
  调用任意方法    
   
        我们不仅可以设置Bean的属性,而且还可以调用堆栈内对象的任意方法。这通过CallMethodRule完成,我们只需指定方法名字,如有必要,再说明调用的参数类型和数量。CallParamRule用来定义传递给被调用函数的参数值,参数值可以从当前XML元素的命名的属性获取,也可以从当前元素包含的原始字符数据获取。例如,在前面实现DigesterDriver的例子中,我们可以不用BeanPropertySetterRule,而是通过显式调用属性的set方法达到同样的目的:     

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


      上面的第一行代码给出了要调用的方法(即setAuthor()),以及该调用需要的参数数量(即1)。第二行代码的意思是从元素包含的字符数据获取函数参数的值,把它作为参数数组的第一个传入(即索引是0的数组元素)。如果我们指定了XML元素属性的名称(例如digester.addCallParam(   "catalog/book/author",   0,   "author"   );),则参数值将从当前元素的相应属性值获取。    
       这里必须注意的是,“digester.addCallMethod(   "pattern",   "methodName",   0   );”这个语句不是指定了一个不带参数的方法调用,而是指定了带有一个参数的方法调用,它的值就是当前XML元素的字符数据!这样,我们又有了另一种替代BeanPropertySetterRule的办法:     

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

    
       如果要调用一个确实没有参数的方法,必须采用如下形式:digester.addCallMethod(   "pattern",   "methodName"   );。    
   
  标准规则概要    
   
  下面简要说明所有标准规则。    
  创建    
  ObjectCreateRule:利用指定类的默认构造函数,创建该类的一个对象,并把对象压入栈。当元素处理结束时,对象被弹出。被实例化的类可通过class对象或类的全称给出。    
  FactoryCreateRule:利用指定的工厂类创建一个对象,把对象压入栈。对于没有提供默认构造函数的类,这一规则很有用。用于该规则的工厂类必须实现org.apache.commons.digester.ObjectCreationFactory接口。    
  设置属性    
  SetPropertiesRule:利用指定名称的XML元素属性值,设置顶层Bean的一个或者多个指定名称的属性。XML元素的属性名称和Bean的属性名称以String[]数组形式传入该规则(通常用来处理之类的结构)。    
  BeanPropertySetterRule:把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。(通常用来处理 <page></page> 10之类的结构)。    
  SetPropertyRule:设置顶层Bean的一个属性。无论是Bean属性的名称,还是赋予该属性的值,都在当前XML元素中以属性的形式指定,例如:<article value="10" key="page"></article>。    
管理父/子关系    
  SetNextRule:弹出栈顶的对象,把它传递给紧接其下的另一个对象的指定名称的方法。通常用来把一个已经初始化的Bean插入到父对象。    
  SetTopRule:把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setParenet方法时,这一规则很有用。    
  SetRootRule:调用栈底对象的一个方法,并把栈顶的对象作为参数传入。    
  调用任意方法    
  CallMethodRule:调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数,参数的值通过后继的CallParamRule给出。    
  CallParamRule:表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性,或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。    
   
  通过XML指定规则    
   
  在前面的内容中,我们用程序代码的方式指定模式和规则,这些模式和规则都是在编译的时候就已经确定,虽然从概念上来讲比较简单,但却不能说尽善尽美:Digester框架的总体目标是在运行时识别和处理各种数据结构,但如果我们用编程的方法指定模式和规则,则所有行为在编译时已经固定!如果Java源程序中包含了大量固定的字符串,通常意味着程序在执行某些配置操作,这部分操作可以被(或许是应该被)延迟到运行时进行。    
  org.apache.commons.digester.xmlrules包解决了这个问题。这个包提供了一个DigesterLoader类,它能够从XML文档读取模式/规则对,返回配置好的Digester对象。用来配置Digester对象的XML文档必须遵从digester-rules.dtd,这个DTD是xmlrules包的一部分。    
  下面就是本文例子的配置文件rules.xml。有几点必须说明。    
  首先,模式可以用两种方式指定:或者使用 <pattern></pattern> 元素,或者通过代表规则的XML元素的属性。这两种办法可以混合使用,且 <pattern></pattern> 元素是可以嵌套的。其次,<alias></alias>元素和<set-properties-rule></set-properties-rule>一起使用,用来把XML属性映射到Bean属性。最后,就当前发行的Digester软件包而言,我们不能在配置文件中指定BeanPropertySetterRule,正如前面所介绍的,我们用CallMethodRule来达到同样的目标。     

  1. xml   version="1.0"?>      
  2.   <digester-rules>      
  3.       <object-create-rule   pattern="catalog"   classname="Catalog"   />      
  4.       <set-properties-rule   pattern="catalog"   >      
  5.           <alias   attr-name="library"   prop-name="library"   />      
  6.       set-properties-rule>      
  7.       <pattern   value="catalog/book">      
  8.           <object-create-rule   classname="Book"   />      
  9.           <call-method-rule   pattern="author"   methodname="setAuthor"   paramcount="0"   />      
  10.           <call-method-rule   pattern="title"   methodname="setTitle"     paramcount="0"   />      
  11.           <set-next-rule   methodname="addBook"   />      
  12.       pattern>      
  13.       <pattern   value="catalog/magazine">      
  14.           <object-create-rule   classname="Magazine"   />      
  15.           <call-method-rule   pattern="name"   methodname="setName"   paramcount="0"   />      
  16.           <pattern   value="article">      
  17.               <object-create-rule   classname="Article"   />      
  18.               <set-properties-rule>      
  19.                   <alias   attr-name="page"   prop-name="page"   />      
  20.               set-properties-rule>        
  21.               <call-method-rule   pattern="headline"   methodname="setHeadline"   paramcount="0"   />      
  22.               <set-next-rule   methodname="addArticle"   />      
  23.           pattern>      
  24.           <set-next-rule   methodname="addMagazine"   />        
  25.       pattern>      
  26.   digester-rules>      
  27.       


  现在,所有实际的操作都转移到了Digester和DigesterLoader类,XmlRulesDriver类就变得相当简单。运行下面的XmlRulesDriver时,在第一个命令行参数中指定目录文档的名字,在第二个参数中指定rules.xml(注意,DigesterLoader不是从File或者org.xml.sax.InputSource读取rules.xml文件,而是要求指定一个URL,因此,下面代码中File引用被转换成了等价的URL)。     

  1. import   org.apache.commons.digester.*;      
  2.   import   org.apache.commons.digester.xmlrules.*;      
  3.   import   java.io.*;      
  4.   import   java.util.*;      
  5.   public   class   XmlRulesDriver   {      
  6.       public   static   void   main(   String[]   args   )   {      
  7.           try   {      
  8.               File   input   =   new   File(   args[0]   );      
  9.               File   rules   =   new
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值