6.5. Variable Substitution and XML Parsing

Problem

You need to parse XML that contains references to variables, and you need to replace these references with variable values at parse time.

Solution

Use Commons Digester’s MultiVariableExpander and the VariableSubstitutor to expand variable references in an XML document during a parse.

Warning

This recipe explores a feature of Commons Digester—variable substitution—which is available only with a prerelease version of Digester, 1.6-dev. To follow the example in this recipe, you must download a nightly snapshot distribution from http://cvs.apache.org/builds/jakarta-commons/nightly/. Use nightly distributions of Commons components at your own risk; these distributions may contain unresolved bugs.

The following XML document contains four variable references—${email.to}, ${order.id}, ${user.name}, and ${product.name}—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="[email protected]">
  <subject>Purchase Confirmation: ${order.id}</subject>
  <priority>High</priority>
  <message>
    Dear ${user.name}, 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 ${product.name} 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", "[email protected]");
               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: [email protected]

Discussion

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.

See Also

For more information about other new features planned for Digester 1.6, see the current development Javadoc for the Digester at http://jakarta.apache.org/commons/digester/apidocs/index.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.147.193