Apache之Digester (1)

  Digester是apache开源项目Commons中的一个子项目,是一款解析处理XML文档的工具。现在Java领域中流传了很多有关处理XML文档解析的工具,除官方(Sun)的标准的SAX(最新版本2.0),DOM(最新版本3.0,在Tiger版本中集成)外[JAXP只是Sun定义的一组规范接口],其他开源不泛多多,比如Jdom,Dom4j,Castor等等,包括这款Apache的digester。说到这里,你不得不佩服开源组织的强大智慧的结晶,digester处理XML文档基于XML节点树Path的规则,实在给人一种赏心悦目之感,这也是偶一直对其情有独钟的最大理由。废话免了,转入正题。
  刚说到Digester处理是基本类似于XML文档树节点遍历的规则来进行处理,底层处理是采用了SAX,基于事件驱动的模式。举个例子:
  <Company>
    <Technology>
      <name length="4">Corx</name>
      <date>2005.06.27</date>
    </Technology>
    
    <Product>
      <name length="4">Kxcp</name>
      <date>2004.12.29</date>
    </Product>
  </Company>
  
  在digester中,定义了一些规则(rule),对遍历的节点path预先对应好要处理的规则,即当解析器遍历到某个节点的时候,如果发现当前节点有对应的处理规则,调用相应的rule进行处理。举个例子:
  Company/Technology -> ObjectCreatedRule //对象创建规则
  Company/Technology/name -> BeanPropertySetterRule //属性存取规则
  ...
  
  对以上的解释可能还不太明白,不要着急,下面详细解释一下digester的基本原理,喝杯咖啡,慢慢来~
  
  首先看看org.apache.commons.digester.Digester这个类,查看source发现Digester本身继承了DefaultHandler句柄,DefaultHandler句柄是SAX中基于时间驱动的缺省的句柄实现(包含ContentHandler, ErrorHandler, EntityResolver, DTDHandler),这个句柄不用多介绍了吧,相信用过SAX的哥们都明白。:)。刚刚不是说到了Rule了嘛,digester中定义了一个规则处理接口org.apache.commons.digester.Rule,此接口类似于ContentHandler接口中的方法,稍稍有点不同,主要有begin(), body(), end(), finish()方法。而digester缺省定义了许多有效的常用规则,每个规则都实现这个接口, 如果没有什么特殊需求,一般这些规则是够用了,罗列一下:BeanPropertySetterRule, CallMethodRule, CallParamRule, FactoryCreateRule, NodeCreateRule, ObjectCreateRule, ObjectParamRule,PathCallParamRule, SetNestedPropertiesRule, SetNextRule, SetPropertiesRule, SetPropertyRule, SetRootRule, SetTopRule,这些规则的意思稍后说。同时,对这些规则,digester还定义了一个规则的容器接口Rules(抽象类),这个抽象类接口容器容纳规则,并定义了规则匹配的模式,digester实现了一个基本的匹配模式RulesBase,简要看看这个实现中的两个最重要的方法:
  .....
  public void add(String pattern, Rule rule) {
        // to help users who accidently add '/' to the end of their patterns
        int patternLength = pattern.length();
        if (patternLength>1 && pattern.endsWith("/") {
            pattern = pattern.substring(0, patternLength-1);
        }
        
        
        List list = (List) cache.get(pattern);
        if (list == null) {
            list = new ArrayList();
            cache.put(pattern, list);
        }
        list.add(rule);
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }

    }

 还有一个方法:
  ...
  public List match(String namespaceURI, String pattern) {

        // List rulesList = (List) this.cache.get(pattern);
        List rulesList = lookup(namespaceURI, pattern);
        if ((rulesList == null) || (rulesList.size() < 1)) {
            // Find the longest key, ie more discriminant
            String longKey = "";
            Iterator keys = this.cache.keySet().iterator();
            while (keys.hasNext()) {
                String key = (String) keys.next();
                if (key.startsWith("*/") {
                    if (pattern.equals(key.substring(2)) ||
                        pattern.endsWith(key.substring(1))) {
                        if (key.length() > longKey.length()) {
                            // rulesList = (List) this.cache.get(key);
                            rulesList = lookup(namespaceURI, key);
                            longKey = key;
                        }
                    }
                }
            }
        }
        if (rulesList == null) {
            rulesList = new ArrayList();
        }
        return (rulesList);
    }
    ...
    
    以上基本实现只是digester默认匹配规则,如果你要更换自己的规则匹配模式,则只需要继承org.apache.commons.digester.Rules接口,定义自己的匹配方式,digester同时还给我们提供了一个比较复杂,不过非常常用的匹配模式,那就是通配符匹配模式,引入了”!“、”*“、”?“三个符号进行通配的匹配模式,这个类就是org.apache.commons.digester.ExtendsBaseRules,后续再说。
    
    digester就是通过以上的几种接口组件,同时配合操作数栈,进行XML解析。具体说,就是在parse XML文档之前,预先向容器集合(默认就是RulesBase容器)对XML文档中的节点path注入匹配规则,然后在parse文档的时候,遭遇到节点时时,调用SAX句柄中相应的方法,配合操作数栈,根据定义好的匹配模式,调用相应规则中的方法,将XML序列化成Java Object。介绍有点抽象,沿用digester本身带的例子介绍一下:
    ...
    //对如下的XML文档
    <address-book>
      <person id="1" category="acquaintance">
        <name>Gonzo</name>
        <email type="business">gonzo@muppets.com</email>
      </person>
      <person id="2" category="rolemodel">
        <name>Kermit</name>
        <email type="business">kermit@muppets.com</email>
        <email type="home">kermie@acme.com</email>
      </person>
    </address-book>
    ...
...
    
    Digester digester = new Digester();
    
    AddressBook book = new AddressBook();
    d.push(book); //将AddressBook实例压入堆栈
    
    digester.addObjectCreate("address-book/person", Person.class);//对person节点注入对象创建规则,即在SAX的事件遭遇到person节点的时候,创建Person类的实例,并压入堆栈,此时堆栈中从栈顶到栈底分别为AddressBook实例,Person类实例。
    digester.addSetProperties("address-book/person";//对person节点注入属性设置规则,即在SAX的事件遭遇到person节点中的Attributes时,根据属性列表中的属性值对,这儿就是id="1", category="acquaintance",使用Java反射(reflection)机制,调用当前栈顶对象即Person实例类中id、category属性的标准的JavaBean方法,setId, setCategory。
    
    digester.addSetNext("address-book/person", "addPerson";//对person节点注入父节点方法调用规则,即在SAX事件遭遇到person节点的时候,调用栈中Person实例的父实例中的addPerson方法。d.addCallMethod("address-book/person/name", "setName", 0);//对name节点注入方法调用规则,调用当前栈顶对象即Person实例中的setName方法,而此方法的参数即是当前name节点的字符内容。通常这个规则和addCallParam规则配合使用,这儿是一种特殊情况。
    digester.addCallMethod("address-book/person/email", "addEmail", 2);//对email节点注入方法调用规则,调用当前栈顶对象即Person实例中的addEmail方法,此方法需要两个参数,一个是从属性值的type属性获取,一个是从email本身的字符内容获取。
    digester.addCallParam("address-book/person/email", 0, "type";//对email节点注入参数调用规则,将当前节点的type属性值压入方法操作数栈
    digester.addCallParam("address-book/person/email", 1);//对email节点注入参数调用规则,将当前节点的字符属性值压入方法操作数栈。
    
    System.out.println(book);//打印book中值。。
    ...
    
    通过以上注释应该不难理解吧。
    
    下面再对以上所说的几种常用规则作一个详细的介绍:
    ObjectCreateRule:这个规则比较简单,此规则就是对指定的模式创建一个类的实例,并将当前实例压入堆栈,并且在遭遇元素结束
    时,将当前的栈顶实例弹出栈。
    对应Digester中有关这个规则的Javadoc方法说明:
      addObjectCreate(java.lang.String pattern, java.lang.Class clazz) - 方法参数说明了一切
      addObjectCreate(java.lang.String pattern, java.lang.String className) - 同上
      addObjectCreate(java.lang.String pattern, java.lang.String attributeName, java.lang.Class clazz) - 这个稍微解释一下,
      多了一个参数attributeName,这个参数的意思就是如果在当前匹配模式的节点中定义了属性,则默认就采用这个attributeName所
      对应的值来加载实例。比如以上面的例子加入person元素还有一个属性, class="test.org.apache.commons.digester.Person1",
      则此规则会加载Person1实例而不是Person实例。明白?!
      addObjectCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName) -同上
      
    FactoryCreateRule:这个规则是基于工厂模式创建指定模式的一个类的实例。跟ObjectCreateRule类似,不同的是其参数Class继承了
    ObjectCreationFactory接口。此接口中有个方法createObject(Attributes attrs),创建类的实例,在规则中将此类的实例压入堆栈。
    对应Digester中有关这个规则的Javadoc方法说明:
      addFactoryCreate(java.lang.String pattern, java.lang.Class clazz) - 一目了然,不用说了。只是clazz必须是实现了
      ObjectCreationFactory接口的类。
      addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, boolean ignoreCreateExceptions) - 同上,多了一个参
      数,ignoreCreateExceptions表明是否忽略在创建类的过程中忽略抛出的exception。
      addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, java.lang.String attributeName) - 稍微介绍一下这里
      的attributeName参数,这个参数跟ObjectCreationRule规则中的attributeName雷同,不同的是这个属性的值必须是实现了接口
      ObjectCreationFactory接口的类。
      addFactoryCreate(java.lang.String pattern, java.lang.Class clazz, java.lang.String attributeName, boolean ignoreCreateExceptions) 
      - 同上
      addFactoryCreate(java.lang.String pattern, ObjectCreationFactory creationFactory) - 同上
      addFactoryCreate(java.lang.String pattern, ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) - 同上
      addFactoryCreate(java.lang.String pattern, java.lang.String className) - 同上
      addFactoryCreate(java.lang.String pattern, java.lang.String className, boolean ignoreCreateExceptions) - 同上
      addFactoryCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName) - 同上
      addFactoryCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName, boolean ignoreCreateExceptions)
       - 同上


 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值