Making things generic using templates
So far you've looked at the mechanics of retrieving an XML document, finding individual records, and outputting them. That's all well and good, but in the long run, not much more useful than simply retrieving the document and displaying it directly. In this section, you start the process of massaging the data into a more desirable form using templates.
The ultimate goal is to create an application than enables users to make choices about content to display, so the last thing you want to do is create a system that requires programming to change the appearance of data. To get around that problem, you can create templates into which the live data can be inserted.
For example, you can create a template such as the one in Listing 10.
Listing 10. A sample template
<p><b><value/></b>: <value/></p>
|
This template takes two values, which you can then replace with live data. For example, Listing 11 shows two different uses of this simple template.
Listing 11. Two different uses of the same template
<p><b>The West Wing</b>: When a
uranium-bearing rig crashes in an Idaho tunnel, the White House scrambles
to assess the potential crisis; election strategists mull dropping Vice
President Hoynes (Tim Matheson) from the Democratic ticket.</p>
<p><b>One killed, one missing in Ohio storms; rare twister
in New York state</b>: (Undated-AP) July 13, 2006 - At least
one person died after up to nine inches of rain brought flooding to Indiana
and Ohio, while tornado north of New York City partly collapsed a commercial
building and ripped the roof off a hotel.</p>
|
In the first case, this is television data and in the second, it's search results. The difference is their source documents and the XPath expressions that represent what was added. In the first case it was the /tv/programme/title and /tv/programme/desc values from the XMLTV feed. In the second it was the /ResultSet/Result/Title and /ResultSet/Result/Summary values from a Yahoo! search result.
In this section, you'll look at using XPath to insert specific data into a template and then at outputting the resulting data.
To begin, create a Document object out of the template, as you can see in Listing 12.
Listing 12. Creating the template Document
...
Document hostDoc = builder.parse(
new InputSource(new StringReader("<div />")));
Node hostRoot = hostDoc.getDocumentElement();
for (int i=0; i < recordNodes.getLength(); i++) {
String template =
"<p><b><value/></b>:<value/></p>";
Document templateDoc = builder.parse(
new InputSource(new StringReader(template)));
}
DOMSource source = new DOMSource(hostDoc);
StreamResult result = new StreamResult(System.out);
...
|
Here you use the same technique used to create the host document. But instead of a single element, you seed the document with the actual template you want to use for each record in the results.
The next step is to use an XPath expression to locate all of the nodes in the template Document that are elements named value, as you can see in Listing 13.
Listing 13. Finding the nodes to replace
...
for (int i=0; i < recordNodes.getLength(); i++) {
String template =
"<p><b><value/></b>:<value/></p>";
Document templateDoc = builder.parse(
new InputSource(new StringReader(template)));
expression = "//value";
NodeList templateNodes =
(NodeList) xpath.evaluate(
expression, templateDoc, XPathConstants.NODESET);
for (int j=0; j < templateNodes.getLength(); j++){
}
}
DOMSource source = new DOMSource(hostDoc);
...
|
The XPath expression represents all value elements in the Document, so when you evaluate it against the templateDoc object, you get a NodeList that points to all of the value elements in that Document. You can then loop through each of those Nodes and take action.
The action that you want to take is to replace those elements with specific data, so the next step is to find that data. In Listing 14, you look for it using specific XPath expressions.
Listing 14. Finding the replacement data
...
for (int i=0; i < recordNodes.getLength(); i++) {
String template =
"<p><b><value/></b>:<value/></p>";
Document templateDoc = builder.parse(
new InputSource(new StringReader(template)));
String[] elementValues = {"title[@lang='en']",
"desc[@lang='en']"};
expression = "//value";
NodeList templateNodes =
(NodeList) xpath.evaluate(
expression, templateDoc, XPathConstants.NODESET);
for (int j=0; j < templateNodes.getLength(); j++){
if ( (Boolean)xpath.evaluate(elementValues[j],
recordNodes.item(i),
XPathConstants.BOOLEAN)) {
Node valueNode =(Node)xpath.evaluate(
elementValues[j],
recordNodes.item(i),
XPathConstants.NODE);
String replacementValue = valueNode.getTextContent();
}
}
}
DOMSource source = new DOMSource(hostDoc);
...
|
You have two slots in the template, so you'll define two different XPath expressions to fill them. For each record node, first check to see whether the record node (recordNodes.item(i)) contains the target expression (elementValues[j]). (The context node is /tv/programme, so the XPath expression is relative to that.)
If the node exists, you can then use the evaluate() method, along with the XPathConstants.NODE constant, to retrieve the node itself. You can then extract the actual text of the Node so you can add it to the template.
Add the replacements to the template
Now that you have the replacement text, you can add it to the template, as in Listing 15.
Listing 15. Adding replacement values to the template
...
expression = "//value";
NodeList templateNodes = (NodeList) xpath.evaluate(
expression, templateDoc, XPathConstants.NODESET);
for (int j=0; j < templateNodes.getLength(); j++){
Node thisTemplateNode = templateNodes.item(j);
Node parentNode = thisTemplateNode.getParentNode();
if ( (Boolean)xpath.evaluate(elementValues[j],
recordNodes.item(i), XPathConstants.BOOLEAN)) {
Node valueNode = (Node)xpath.evaluate(
elementValues[j], recordNodes.item(i),
XPathConstants.NODE);
String replacementValue = valueNode.getTextContent();
Node replacementNode =
templateDoc.createTextNode(replacementValue);
parentNode.replaceChild(replacementNode,
thisTemplateNode);
}
}
...
|
For each template node to be replaced, first obtain a reference to that specific node. Then get a reference to that node's parent, which will ultimately enable you to replace the template node.
Once you determine that a replacement value exists and retrieve that value,you first create a new text node within the templateDoc. That node contains the replacement value. You can then use the replaceChild() method to replace the original value element (thisTemplateNode) with the new replacement text node. The result is a template that contains the new information.
Add the new template to the output document
Once you have the completed template, add it to the output document, just as you added the raw data in the previous section, as in Listing 16.
Listing 16. Adding the completed template to the output document
...
for (int j=0; j < templateNodes.getLength(); j++){
...
parentNode.replaceChild(replacementNode,
thisTemplateNode);
}
}
Node importedNode = hostDoc.importNode(
templateDoc.getDocumentElement(), true);
hostRoot.appendChild(importedNode);
hostRoot.appendChild(hostDoc.createTextNode("\n"));
}
DOMSource source = new DOMSource(hostDoc);
StreamResult result = new StreamResult(System.out);
...
|
For each record, process the template and then add it to the host document just as you previously added the raw document. Once that process is complete, output the host document as usual. For the abbreviated TV listings document, the results look like Listing 17.
Listing 17. The final results
<?xml version="1.0"
encoding="UTF-8"?><div><p><b>Flintstones</b>
:Fred gets fired and enters the pie-baking business.</p>
<p><b>Dastardly & Muttley</b>:<value/></p>
<p><b>Banana Splits</b>:<value/></p>
<p><b>Project Runway</b>:<value/></p>
<p><b>Project Runway</b>:<value/></p>
<p><b>Celebrity Poker Showdown</b>:Angie Dickinson, Penn
Jillette, Jeff Gordon, Kathy Griffin and Ron Livingston compete on behalf
of their favorite charities.</p>
<p><b>Celebrity Poker Showdown</b>:Willie Garson, Jennie
Garth, Richard Kind, Dave Navarro and Jerry O'Connell compete on behalf of
their favorite charities.</p>
<p><b>Kathy Griffin: My Life on the D-List</b>:Kathy's
trip to entertain the troops is winding down.</p>
<p><b>Kathy Griffin: My Life on the D-List</b>:Kathy has
problems with her new puppy; the Learning Annex hires Kathy to teach a
class.</p>
<p><b>Project Runway</b>:<value/></p>
<p><b>Project Runway</b>:<value/></p>
<p><b>The West Wing</b>:When a uranium-bearing rig
crashes in an Idaho tunnel, the White House scrambles to assess the
potential crisis; election strategists mull dropping Vice President
Hoynes (Tim Matheson) from the Democratic ticket.</p>
</div>
|
Notice that the template still contains value elements in cases in which the data wasn't found in the source document. Browsers will ignore them, but you also have the option to put them into separate namespaces, or even to remove them altogether before output. (For example, that might be a good application for XSL transformations.)




