Search code examples
javaxmlapache-commons-digester

Apache Digester XML roule to invoke a custom setter on a bean


I am trying to parse a big XML file using Apache commons-digester3. I am interested in extract only certain data. The XML is quite complex and I would prefer not to build the whole structure but rather match the patterns I am interested in.

Say I have the following XML:

<?xml version="1.0"?>
<foo>
    <level1>
        <level2>
            <bar>the bar</bar>
        </level2>
    </level1>
</foo>

And I have the following domain object that I want to parse into:

package my.pkg;

public class Foo {
    private String bar;

    // Note the name of the setter is not "setBar" but rather "setTheBar"
    public void setTheBar(String bar) {
        this.bar = bar;
    }
}

And now I have the XML rules I am having problems to get them right:

<digester-rules>
    <pattern value="foo">
        <object-create-rule classname="my.pkg.Foo"/>
        <pattern value="level1/level2/bar">
            <!--What do I need to pout on here the get "the bar" value injected into my Foo instance-->
        </pattern>
    </pattern>
</digester-rules>

I tried all sorts of combinations of set-method-rule, bean-property-setter-rule, etc but all failed to give me what I wanted. What I want seems so basic I am sure the solution must be so obvious but I cannot get it.

Thank you in advance for your help.


Solution

  • I think a call-method-rule with "usingElementBodyAsArgument='true'" should work for you:

    <digester-rules>
      <pattern value='foo'>
      <object-create-rule classname='my.pkg.Foo'/>
        <pattern value='level1/level2/bar'>
          <call-method-rule methodname='setTheBar'
                            usingElementBodyAsArgument='true'/>
        </pattern>
      </pattern>
    </digester-rules>
    

    However, I'd always note that XML rules are fairly limited - they can do all the basic stuff but miss quite a lot of more advanced functionality (custom rules etc.), and if you use digester regularly you may well find yourself moving from XML rules to programmatic rules at some point. Personally, I always use the programmatic rules now, as I know I can always do everything I need.

    The programmatic equivalent of the XML rules above would be:

    RulesModule rules = new AbstractRulesModule() {
      @Override
      public void configure() {
        forPattern("foo")
            .createObject().ofType(Foo.class);
        forPattern("foo/level1/level2/bar")
            .callMethod("setTheBar")
            .withParamCount(1)
            .usingElementBodyAsArgument();
      }
    };
    
    DigesterLoader loader = DigesterLoader.newLoader(rules);
    Digester digester = loader.newDigester();
    
    Foo foo = (Foo)digester.parse(...);
    

    Hope this helps.