You need to parse XML that contains references to variables, and you need to replace these references with variable values at parse time.
Use Commons Digester's MultiVariableExpander
and the VariableSubstitutor
to expand variable references in an XML document during a
parse.
The following XML document contains four variable
references—${email.to}
, org.codehaus.cjcook:cjcook-content:jar:0.19
, root
, and Common Java Cookbook DocBook XML Content—
all of which need to be
replaced with values before the XML is parsed by the Digester
:
<?xml version="1.0"?> <email to="${email.to}" from="ceo@xyzblah.com"> <subject>Purchase Confirmation: org.codehaus.cjcook:cjcook-content:jar:0.19</subject> <priority>High</priority> <message> Dear root, we appreciate your business. As CEO of Big Software Company, Inc., I would like to personally thank you for helping us become filthy rich. Your purchase of Common Java Cookbook DocBook XML Content helped me purchase an even larger boat for myself. Thanks again. </message> </email>
This document represents a purchase confirmation message, and your
system needs to unmarshall the above message to the following class,
Email
:
public class Email { private String to; private String from; private String subject; private String priority; // accessors omitted for brevity. }
The following XML rule set is similar to the rule set defined in
Recipe 6.2. When the parser
hits an email
element, an instance of
Email
is created and properties are
populated:
<?xml version="1.0"?> <digester-rules> <pattern value="email"> <object-create-rule classname="com.discursive.jccook.xml.bean.Email"/> <set-next-rule methodname="add" paramtype="java.lang.Object"/> <set-properties-rule/> <bean-property-setter-rule pattern="subject"/> <bean-property-setter-rule pattern="priority"/> <bean-property-setter-rule pattern="message"/> </pattern> </digester-rules>
This difference between this recipe and Recipe 6.2 is that our XML
document contains variables to be replaced at parse time. The following
code creates a Map
that contains
variables referenced in the XML document being parsed. This code creates
the variable Map
, reads the XML rule
set from email-rules.xml
, and
parses the XML document email.xml
:
import org.apache.commons.digester.Digester; import org.apache.commons.digester.Substitutor; import org.apache.commons.digester.substitution.MultiVariableExpander; import org.apache.commons.digester.substitution.VariableSubstitutor; import org.apache.commons.digester.xmlrules.DigesterLoader; // Read the Digester XML rule set and create Digester URL rules = getClass( ).getResource("./email-rules.xml"); Digester digester = DigesterLoader.createDigester(rules); // Create object to push onto Digester Stack List emails = new ArrayList( ); digester.push( emails ); // Create Map of variables Map vars = new HashMap( ); vars.put("email.to", "ldavid@hbo.com"); vars.put("user.name", "Tim"); vars.put("order.id", "1RR2E223WVVS" ); vars.put("product.name", "Foundation" ); // Create an expander with the Map that matches ${var} MultiVariableExpander expander = new MultiVariableExpander( ); expander.addSource("$", vars); // Create a substitutor with the expander Substitutor substitutor = new VariableSubstitutor(expander); digester.setSubstitutor(substitutor); // Parse XML document InputStream input = getClass( ).getResourceAsStream("./email.xml"); digester.parse( input ); // Retrieve Email object Email email = (Email) emails.get(0); System.out.println( "Email Subject: " + email.getSubject( ) ); System.out.println( "Email To: " + email.getTo( ) );
Variable substitution is performed by a VariableSubstitutor
that has been configured
with a MultiVariableExpander
. The
MultiVariableExpander
retrieves
variables from a Map
, and, in this
example, the addSource( )
method is
called with a $
marker. This means
that variables are referenced by surrounding a variable name with
${
and }--${variable}
. The previous example produces
the following output, which demonstrates the substitution of
variables:
Email Subject: Purchase Confirmation: 1RR2E223WVVS Email To: ldavid@hbo.com
Variable substitution is part parsing, part templating, and can be valuable when you need to specify a set of default properties on a bean that need to be parameterized for different situations. The example in this recipe was an email message confirming a purchase from an e-commerce system, but there are other situations where an object may need to be personalized with a user identifier or a username.
For more information about other new features planned for Digester 1.6, see the current development Javadoc for the Digester at http://commons.apache.org/digester/apidocs/index.html.