The legacy XML wrapper is deprecated.
FreeMarker 2.3 has introduced support for a new XML processing
model. To support this, a new XML wrapper package was introduced,
freemarker.ext.dom
. For new usage, we encourage
you to use that. It is documented in the part XML Processing Guide.
The class freemarker.ext.xml.NodeListModel
provides a template model for wrapping XML documents represented as
node trees. Every node list can contain zero or more XML nodes
(documents, elements, texts, processing instructions, comments, entity
references, CDATA sections, etc.). The node list implements the
following template model interfaces with the following
semantics:
TemplateScalarModel
When used as a scalar, the node list will render the XML fragment that represents its contained nodes. This makes it handy for use in XML-to-XML transforming templates.
TemplateCollectionModel
When used as a collection with list
directive, it will simply enumerate its nodes. Every node will be
returned as a new node list consisting of a single node.
TemplateSequenceModel
When used as a sequence, it will return the i-th node as a new
node list consisting of the single requested node. I.e. to return
the 3rd <chapter>
element of the
<book>
element, you'd use the following
(note indexes are zero-based):
<#assign thirdChapter = xmldoc.book.chapter[2]>
TemplateHashModel
When used as a hash, it is basically used to traverse
children. That is, if you have a node list named
book
that wraps an element node with several
chapters, then the book.chapter
will yield a node
list with all chapter elements of that book element. The at sign is
used to refer to attributes: book.@title
yields a
node list with a single attribute node, that is the title attribute
of the book element.
It is important to realize the consequence that, for example,
if book
has no chapter
-s then
book.chapter
is an empty sequence, so
xmldoc.book.chapter??
will
not be false
, it will be
always true
! Similarly,
xmldoc.book.somethingTotallyNonsense??
will not
be false
either. To check if there was no
children found, use xmldoc.book.chapter?size ==
0
.
The hash defines several "magic keys" as well. All these keys
start with an underscore. The most notable is the
_text
key which retrieves the text of the node:
${book.@title._text}
will render the value of the
attribute into the template. Similarly, _name
will retrieve the name of the element or attribute.
*
or _allChildren
returns all
direct children elements of all elements in the node list, while
@*
or _allAttributes
returns
all attributes of the elements in the node list. There are many more
such keys; here's a detailed summary of all the hash keys:
Key name | Evaluates to |
---|---|
* or _children |
all direct element children of current nodes (non-recursive). Applicable to element and document nodes. |
@* or
_attributes |
all attributes of current nodes. Applicable to elements only. |
@attributeName |
named attributes of current nodes. Applicable to elements,
doctypes and processing instructions. On doctypes it supports
attributes publicId ,
systemId and
elementName . On processing instructions, it
supports attributes target and
data , as well as any other attribute name
specified in data as name="value" pair. The
attribute nodes for doctype and processing instruction are
synthetic, and as such have no parent. Note, however that
@* does NOT operate on doctypes or
processing instructions. |
_ancestor |
all ancestors up to root element (recursive) of current
nodes. Applicable to same node types as
_parent . |
_ancestorOrSelf |
all ancestors of current nodes plus current nodes.
Applicable to same node types as
_parent . |
_content |
the complete content of current nodes, including children elements, text, entity references, and processing instructions (non-recursive). Applicable to elements and documents. |
_descendant |
all recursive descendant element children of current nodes. Applicable to document and element nodes. |
_descendantOrSelf |
all recursive descendant element children of current nodes plus current nodes. Applicable to document and element nodes. |
_document |
all documents the current nodes belong to. Applicable to all nodes except text. |
_doctype |
doctypes of the current nodes. Applicable to document nodes only. |
_filterType |
is a filter-by-type template method model. When called, it will yield a node list that contains only those current nodes whose type matches one of types passed as argument. You should pass arbitrary number of strings to this method containing the names of types to keep. Valid type names are: "attribute", "cdata", "comment", "document", "documentType", "element", "entity", "entityReference", "processingInstruction", "text". |
_name |
the names of current nodes, one string per node (non-recursive). Applicable to elements and attributes (returns their local names), entities, processing instructions (returns its target), doctypes (returns its public ID) |
_nsprefix |
the namespace prefixes of current nodes, one string per node (non-recursive). Applicable to elements and attributes |
_nsuri |
the namespace URIs of current nodes, one string per node (non-recursive). Applicable to elements and attributes |
_parent |
parent elements of current nodes. Applicable to element, attribute, comment, entity, processing instruction. |
_qname |
the qualified names of current nodes in
[namespacePrefix:]localName form, one
string per node (non-recursive). Applicable to elements and
attributes |
_registerNamespace(prefix, uri) |
register a XML namespace with the specified prefix and URI
for the current node list and all node lists that are derived
from the current node list. After registering, you can use the
nodelist["prefix:localname"] , or
nodelist["@prefix:localname"] syntax (or
nodelist.prefix\:localname , or
nodelist.@prefix\:localname ) to reach
elements, and attributes whose names are namespace-scoped.
Note that the namespace prefix need not match the actual
prefix used by the XML document itself since namespaces are
compared solely by their URI. Also note that if you do
doc.elem1._registerNamespace(...) ,
and then later you use doc.elem1 again, it
will not have the prefix registered, because each time you use
doc.elem1 , it gives a completely new
object. In this example, you certainly should have used
doc._registerNamespace(...) . |
_text |
the text of current nodes, one string per node (non-recursive). Applicable to elements, attributes, comments, processing instructions (returns its data) and CDATA sections. The reserved XML characters ('<' and '&') are not escaped. |
_type |
Returns a node list containing one string per node describing the type of the node. Possible node type names are: "attribute", "cdata", "comment", "document", "documentType", "element", "entity", "entityReference", "processingInstruction", "text". If the type of the node is unknown, returns "unknown". |
_unique |
a copy of the current nodes that keeps only the first
occurrence of every node, eliminating duplicates. Duplicates
can occur in the node list by applying uptree-traversals
_parent , _ancestor ,
_ancestorOrSelf , and
_document . I.e.
foo._children._parent will return a node
list that has duplicates of nodes in foo - each node will have
the number of occurrences equal to the number of its children.
In these cases, use
foo._children._parent._unique to eliminate
duplicates. Applicable to all node types. |
any other key | element children of current nodes with name matching the
key. This allows for convenience child traversal in
book.chapter.title style syntax. Note that
nodeset.childname is technically equivalent
to nodeset("childname") , but is both
shorter to write and evaluates faster. Applicable to document
and element nodes. |
TemplateMethodModel
When used as a method model, it returns a node list that is
the result of evaluating an XPath expression on the current contents
of the node list. For this feature to work, you must have the
Jaxen
library in your classpath. For
example:
<#assign firstChapter=xmldoc("//chapter[first()]")>
Namespace handling
For purposes of traversal of children elements that have namespace-scoped names, you can register namespace prefixes with the node list. You can do it either in Java, calling the
public void registerNamespace(String prefix, String uri);
method, or inside a template using the
${nodelist._registerNamespace(prefix, uri)}
syntax. From there on, you can refer to children elements in the namespace denoted by the particular URI through the syntax
nodelist["prefix:localName"]
and
nodelist["@prefix:localName"]
as well as use these namespace prefixes in XPath expressions. Namespaces registered with a node list are propagated to all node lists that are derived from the original node list. Note also that namespaces are matched by their URI only, so you can safely use a prefix for a namespace inside your template that differs from the prefix in the actual XML document - a prefix is just a local alias for the URI both in the template and in the XML document.