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).