基本内容

Note:

这部分使用的DOM树和变量是 前一章节 中做好的。

因为XML处理的方法非常必要--这在前面章节中已经展示-- 编写一个FTL程序来遍历树,为了找到不同种类的结点。而使用声明的方法, 宁愿定义如何控制不同种类的结点,之后让 FreeMarker 遍历那棵树, 调用你定义的处理器。这个方法对于复杂的XML模式非常有用, 相同元素可以作为其他元素的子元素出现。 这样的模式的示例就是XHTML和XDocBook。

最经常使用来处理声明方式的指令就是 recurse 指令, 这个指令获取结点变量,并把它作为是参数,从第一个子元素开始, 一个接一个地"访问"所有它的子元素。"访问"一个结点意味着它调用了用户自定义的指令 (比如宏),它的名字和子结点(?node_name)的名字相同。 我们这么说,用户自定义指令操作结点。使用用户自定义指令 处理 的结点作为特殊变量 .node 是可用的。例如,这个FTL:

<#recurse doc>

<#macro book>
  I'm the book element handler, and the title is: ${.node.title}
</#macro>

将会输出(这里已经移除了输出内容中一些烦扰的空白):

I'm the book element handler, and the title is: Test Book

如果调用 recurse 而不用参数,那么它使用 .node,也就是说,它访问现在处理这个结点的所有子结点。 所以这个FTL:

<#recurse doc>

<#macro book>
  Book element with title ${.node.title}
    <#recurse>
  End book
</#macro>

<#macro title>
  Title element
</#macro>

<#macro chapter>
  Chapter element with title: ${.node.title}
</#macro>

将会输出(这里已经移除了输出内容中一些烦扰的空白):

Book element with title Test Book
Title element
Chapter element with title: Ch1
Chapter element with title: Ch2
End book

已经看到了如何来为元素结点定义处理器,但不是为文本结点定义处理器。 因为处理器的名字是和它处理的结点名字相同的,作为所有文本结点的结点名字是 @text(参考该表), 为文本结点定义处理器,可以是这样的:

<#macro @text>${.node?html}</#macro>

请注意 ?html。不得不转义HTML文本, 因为生成的是HTML格式的输出。

这个模板就是转换XML到完整的HTML:

<#recurse doc>

<#macro book>
  <html>
    <head>
      <title><#recurse .node.title></title>
    </head>
    <body>
      <h1><#recurse .node.title></h1>
      <#recurse>
    </body>
  </html>
</#macro>

<#macro chapter>
  <h2><#recurse .node.title></h2>
  <#recurse>
</#macro>

<#macro para>
  <p><#recurse>
</#macro>

<#macro title>
  <#--
    We have handled this element imperatively,
    so we do nothing here.
  -->
</#macro>

<#macro @text>${.node?html}</#macro>

将会输出(这里包含了那些烦扰的空白):

  <html>
    <head>
      <title>Test Book</title>
    </head>
    <body>
      <h1>Test Book</h1>


    <h2>Ch1</h2>


      <p>p1.1

      <p>p1.2

      <p>p1.3


    <h2>Ch2</h2>


      <p>p2.1

      <p>p2.2


    </body>
  </html>

  

请注意,可以在输出中使用trim 指令,如 <#t> 来大幅减少多余的空白。参考:模板开发指南/其它/空白处理

你也许会说FTL处理它的这些必要方法可以更短些。那是对的, 但是示例XML使用了非常简单的模式,正如之前说过的, 声明方法带和XML模式一同来了它的格式, 而这个模式关于这里可以出现什么元素是不固定的。所以, 介绍元素 mark, 应该把文本标记为红色, 而和你在哪儿使用它无关;在 title 或在 para 中。对于这点,使用声明的方法,你可以增加一个宏:

<#macro mark><font color=red><#recurse></font></#macro>

那么 <mark>...</mark> 将会自动起作用。 所以对于命令式的XML模式,声明的XML处理确实将会很短,而且更重要的是, 对于必要的XML处理,FTL-s会更清晰。但这都依赖于你的决定,什么时候使用哪种方法; 不要忘记你可以自由混合两种方法。也就是说,在一个元素处理器中, 你可以使用命令式的方法来处理元素的内容。