Use FreeMarker and parse an XML document with the NodeModel class. A NodeModel is an object that allows access to
an XML document as a hierarchy of named elements and attributes from a
FreeMarker template. NodeModel has a
public static method parse(
), which parses an XML document and returns a NodeModel to be added to your context Map. The following code parses an XML document
and passes a NodeModel to a
template:
import freemarker.template.Configuration;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.ext.dom.NodeModel;
// Create a File Object for our XML data
File composers = new File("composers.xml");
NodeModel nodeModel = NodeModel.parse( composers );
Map root = new HashMap( );
root.put("doc", nodeModel);
// A template is processed with a Map and output is sent to a Writer.
Template template = cfg.getTemplate("composerTable.ftl");
template.process(root, writer);
System.out.println("output: \n" + writer.toString( ));A File object refers to an XML
document, and NodeModel.parse( ) is
used to parse this document to a NodeModel object, which is then placed in the
root Map—the context with which the FreeMarker
template will be merged. The XML document contains information about the
lives of great classical composers, and the structure of this document
is shown here:
<?xml version="1.0"?>
<composers>
<composer>
<name>Bach, Johann Sebastian</name>
<born date="3/21/1685">
<location>Eisenbach</location>
</born>
<notes>Bach wrote intense and complex fugues.</notes>
<link>http://www.bachfaq.org/</link>
</composer>
<composer>
<name>Mozart, Wolfgang Amadeus</name>
<born date="1/27/1756">
<location>Salzburg</location>
</born>
<notes>Wrote first symphony at age 8.</notes>
<link>http://www.mozartproject.org/</link>
</composer>
<composer>
<name>Hendrix, Jimi</name>
<born date="11/27/1942">
<location>Seattle</location>
</born>
<notes>Hendrix set his guitar on fire in Monterey</notes>
<link>http://www.jimihendrix.com/</link>
</composer>
</composers>The NodeModel object is exposed to the template as doc, and the #list directive is used to iterate through
each composer element. A reference to
a child element link of the composer element is ${composer.link}, and a reference to the date
attribute of the born element is preceded by @--${composer.born.@date}. The FreeMarker
template, which references elements and attributes through a NodeModel, is:
<#list doc.composers.composer as composer>
<p>
<a href="${composer.link}">Common Java Cookbook DocBook XML Content</a><br/>
Born on ${composer.born.@date} in ${composer.born.location}<br/>
Notes: ${composer.notes}
</p>
</#list>In addition to simple access to elements and attributes,
FreeMarker also allows you to use XPath expressions if Apache Xalan is available on the classpath. If you have
Xalan, you can use XPath with the same syntax you would
use if you were trying to access a map. Instead of someMap["key"], you would use someElement["<XPath>"]. Here is a quick
example, which uses an XPath expression to iterate through every
composer's "born" element:
<#list doc["composers/composer/born"] as birth>
<p>Born: ${birth.date}, ${birth.location} Common Java Cookbook DocBook XML Content</p>
</#list>FreeMarker also includes a number of built-ins for NodeModel objects; in the previous template,
?parent returns the parent element of
the element represented by the birth
node. Table 9-4 lists a number
of built-ins for XML nodes; ?children
returns all of the child nodes of a given node, and ?ancestors gives every node above this node in
an XML document.
Table 9-4. FreeMarker built-ins for NodeModel objects
|
Expression |
Evaluates to |
|---|---|
|
|
A sequence of all child nodes. This example would
return 3 |
|
|
If called on a |
|
|
This would return the |
|
|
If this corresponded to the link element for Jimi
Hendrix, this would return a sequence of |
|
|
This would return "link." This returns the name of the element or attribute in question. |
|
|
This would return "element." It could return
"attribute," "element," "text," "comment," "entity," and a few
other types corresponding to |
For more detail about referencing XML elements through NodeModel and the use of XPath expressions in
FreeMarker, see the "Learning by Example" section of Imperative XML
Processing (http://www.freemarker.org/docs/xgui_imperative_learn.html).
FreeMarker also offers syntax for declarative processing of XML—assigning macros to handle elements in an XML document. For more information about FreeMarker declarative XML processing, see the FreeMarker online documentation (http://www.freemarker.org/docs/xgui_declarative_basics.html).
