| Sign In/My Account | View Cart |
Learning and Using Jakarta Digester
Pages: 1, 2, 3
Below are brief descriptions of all of the standard rules.
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.
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" />.)
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.
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.
|
Related Reading
Programming Jakarta Struts |
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();
}
}
}
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.
Philipp K. Janert is a software project consultant, server programmer, and architect.
Return to ONJava.com.