每一个和DOM树中单独结点对应的变量都是结点和哈希表类型的多类型的变量
(对于程序员:实现TemplateNodeModel
和 TemplateHashModel
两个接口)。因此,你可以使用结点内建函数来处理它们。
哈希表的键被解释为XPath表达式,除了在下面表格中显示的特殊键。
一些结点变量也有字符串类型,所以你可以使用它们作为字符串变量
(对于程序员:他们需要实现 TemplateScalarModel
接口)。
结点类型 (?node_type ) |
结点名称 (?node_name ) |
字符串值 (比如 <p>${node} ) |
特殊哈希表键 |
---|---|---|---|
"document" |
"@document" |
没有字符串值。(当你尝试用字符串来使用时发生错误。) | "elementName" ,
"prefix:elementName" ,
"*" , "**" ,
"@@markup" ,
"@@nested_markup" ,
"@@text" |
"element" |
"name" :
元素的名称。这是本地命名(也就是没有命名空间前缀的名字)。 |
如果它没有子元素,所有子结点的文本串联起来。 当你尝试用它当字符串时会发生错误。 | "elementName" ,
"prefix:elementName" ,
"*" , "**" ,
"@attrName" ,
"@prefix:attrName" ,
"@@" , "@*" ,
"@@start_tag" ,
"@@end_tag" ,
"@@attributes_markup" ,
"@@markup" ,
"@@nested_markup" ,
"@@text" , "@@qname" |
"text" |
"@text" |
文本本身。 | "@@markup" ,
"@@nested_markup" ,
"@@text" |
"pi" |
"@pi$target" |
在目标名称和 ?> 之间的部分。 |
"@@markup" ,
"@@nested_markup" ,
"@@text" |
"comment" |
"@comment" |
注释的文本,不包括分隔符 <!--
和 --> 。 |
"@@markup" ,
"@@nested_markup" ,
"@@text" |
"attribute" |
"name" :
属性的名字。这是本地命名(也就是没有命名空间前缀的名字)。 |
属性的值。 | "@@markup" ,
"@@nested_markup" ,
"@@text" , "@@qname" |
"document_type" |
"@document_type$name" :
name 是文档元素的名字。 |
没有字符串值。(当你尝试用字符串来使用时发生错误。) | "@@markup" ,
"@@nested_markup" ,
"@@text" |
注意:
-
没有CDATA类型,CDATA结点被透明地认为是文本结点。
-
变量 不 支持
?keys
和?values
。 -
元素和属性结点的名字是本地命名,也就是,它们不包含命名空间前缀。 结点所属的命名空间的URI可以使用内建函数
?node_namespace
来获得。 -
XPath表达式需要Jaxen(推荐使用,但是请使用1.1-beta-8之后的版本; 可以从此处下载) 或者Apache的Xalan库,否则会有错误并且终止模板的执行。要注意, 隐藏在XPath表达式中一些特殊哈希表键有相同的意思, 尽管没有XPath可用的实现,但那些XPath表达式仍然会起作用。 如果Xalan和Jaxen两者都可用, FreeMarker 将会使用Xalen,除非你在Java代码中通过调用
freemarker.ext.dom.NodeModel.useJaxenXPathSupport()
方法,才会使用Jaxen。 -
如果Jaxen被用来支持XPath(而不是Xalan), 那么 FreeMarker 变量通过XPath变量的引用是可见的 (比如
doc["book/chapter[title=$currentTitle]"]
)。
特殊哈希表键的含义:
-
"elementName"
,"prefix:elementName"
: 返回元素名为elementName
的子结点的序列。(注意这里的"子"结点就是 直接 后继) 选择是XML命名空间的标识,除非XML文档用不再命名空间标识结点中的XML解析器解析。 在XML命名空间标识结点中,不含前缀的名字(elementName) 仅仅选择不属于任何XML命名空间的元素(除非你已经注册了默认的XML命名空间), 有前缀的名字(prefix:elementName) 选择属于前缀代表命名空间的元素。前缀的注册和默认XML命名空间的设置是由ftl
指令 的ns_prefixes
参数完成的。 -
"*"
:返回所有子(直接后继) 元素 结点的序列。这个序列会按"文档顺序"包含元素, 也就是说,按照每个XML结点的表现形式的第一个字符的顺序出现 (在一般实体的扩展之后)。 -
"**"
:返回所有后继 元素 结点的序列。这个序列按文档顺序包含元素。 -
"@attName"
,"@prefix:attrName"
: 作为一个大小为1,包含属性结点的序列的形式,返回元素的属性名attName
,如果属性不存在时, 作为一个空序列返回(所以来检查属性是否存在,可以使用foo.@attName[0]??
, 而不是foo.@attName??
)。 正如"elementName"
这个特殊的键, 如果序列的长度为1,那么它也当作是第一个子变量。如果没有使用prefix
,那么它只返回属性, 而不使用XML命名空间(尽管你已经设置了默认的XML命名空间)。 如果使用了prefix
, 它返回仅仅属于关联的prefix
的XML命名空间的属性。前缀的注册是由ftl
指令 的ns_prefixes
参数完成的。 -
"@@"
或"@*"
:返回属于父结点的结点的属性序列,这和XPath中的@*
是相同。 -
"@@qname"
:返回元素的完全限定名 (比如e:book
,和由?node_name
返回的本地名book
形成对比)。使用的前缀 (如e
)是基于在当前命名空间用ftl
指令的ns_prefixes
参数注册的前缀而选择的, 而不受源XML文档中使用的前缀影响。如果你已经设置了一个默认的XML命名空间, 那么结点会使用默认的,前缀D
就会被使用了。 不属于任何XML命名空间的结点,不使用前缀(尽管你设置过默认的命名空间)。 如果结点没有为命名空间注册的前缀,那结果是不存在的变量 (node.@@qname??
是false
)。 -
"@@markup"
:这会以字符串形式返回一个结点的完整XML标记。 (完整的XML标记表示它也包含子结点的标记,而且包含子结点的子结点的标记,以此类推) 你得到的标记和在源XML文档中的标记相比不是必须的,它只是语义上的相同。特别地, 注意CDATA段将会解释成普通文本。也要注意基于你是怎么用包装原生的XML文档的, 注释或处理指令结点可能被移除,而且它们将会从源文件的输出中消失。 对于在输出XML段的每个XML命名空间,第一个被输出的开始标记将会包含xmlns:prefix
属性, 而且那些前缀将会在输出的元素和属性名中使用。这些前缀和使用ftl
指令的ns_prefixes
参数注册的前缀相同 (没有前缀使用D
,因为它会和xmlns
属性被用来注册默认的命名空间),如果XML命名空间没有定义前缀, 那么会使用一个任意未被选择使用的前缀。 -
"@@nested_markup"
:这个和"@@markup"
相似,但是它返回不包括开放和封闭标记元素的XML标记。对于文档结点,它返回和"@@markup"
相同的内容。对于其他类型结点(文本,处理指令等), 它返回一个空字符串。不像"@@markup"
,在输出中没有xmlns:prefix
属性, 但是关于在元素和属性名中使用前缀规则是相同的。 -
"@@text"
:它返回文本结点(所有后继文本结点, 而不是直接子结点)点的值,连接成一个单独的字符串。 如果结点没有子文本结点,那么返回的是空字符串。 -
"@@start_tag"
:返回元素结点 开始标签 的标记。 正如@@markup
,输出和源XML文档相比不是必须的, 但是在语义上是相同的。关于XML命名空间 (输出中的xmlns:prefix
属性等), 规则和"@@markup"
是相同的。 -
"@@end_tag"
:返回元素结点 结束标签 的标记,正如@@markup
,输出和源XML文档相比不是必须的, 但是在语义上是相同的。 -
@@attributes_markup
:返回元素结点 属性 的标记,正如@@markup
,输出和源XML文档相比不是必须的, 但是在语义上是相同的。
结点序列
许多特殊哈希表的键(指的是上面所有的列表), 和XPath表达式的结果是结点集(参考XPath 推荐), 返回结点的序列。
这些结点序列,如果它们只存储一个子变量,也会当作子变量本身。
例如,如果 book
元素只有一个 title
子结点,${book.title[0]}
和 ${book.title}
是相同的。
返回一个空结点序列是普通的情形。例如,如果一个确定的XML文档,
book
元素没有子元素 chapter
,
那么 book.chapter
的结果就是一个空的结点序列。
要小心!这也意味着 book.chaptre
(注意打字错误)
也会返回空的结点序列,而且会抛出错误而停止执行。同时,
book.chaptre??
(注意打字错误)将会返回
true
,因为空的序列存在,所以你不得不使用
book.chaptre[0]??
来检查。
结点序列存储的不是1个结点(但是0或者比1多的结点) 也会支持上面所描述的哈希表的键。那就是,下面这些特殊的键都是支持的:
-
"elementName"
,"prefix:elementName"
-
"@attrName"
,"@prefix:attrName"
-
"@@markup"
,"@@nested_markup"
-
"@@text"
-
"*"
,"**"
-
"@@"
,"@*"
当在一个包含多于1个结点或0个结点序列里应用上面这些特殊的键中之一的时候,
那么对于序列(就是特殊的键可以起作用,比如文本结点对于键 *
或 @foo
将会被略过)中的每个结点,
这些特殊的键将会被应用于单独的结点,结果将会从最终的结果中被连接。
结果会被以和结点在序列中出现的对应顺序来连接。
连接就意味着字符串或序列的连接是基于结果类型之上的。
如果特殊的键会得到单独结点的字符串结果,那么对于多个结点的结果就是一个单独的字符串
(对单独结点的结果进行连接),而如果特殊的键返回单独结点的序列,那么对于多个结点,
结果就是一个单独的序列。如果在你应用特殊键的序列中没有结点,那么字符串结果就是空串,
而序列结果就是空序列。
XPath表达式可以和结点序列一同使用。然而,对于0或者大于1的结点, 因为Xalan对XPath实现的限制,仅仅你使用Jaxen代替Xalan时它才会起作用。