Basics

Note:

本部分使用 DOM 树和在上一章中创建的变量。

使用强制性 XML 处理方法(如上一章所示),您可以编写一个 FTL 程序,该程序遍历树以查找不同种类的节点。使用声明性方法,您可以定义如何处理不同类型的节点,然后让 FreeMarker 遍历树并调用您已定义的处理程序。这种方法对于复杂的 XML 模式非常有用,在复杂的 XML 模式中,同一元素可能与许多其他元素的子元素一样出现。这种模式的示例是 XHTML 和 XDocBook。

声明性方法中最常使用的指令是recurse directive。该指令将节点变量作为参数,并从第一个孩子开始一个接一个地“访问”其所有子节点。 “访问”节点意味着它调用一个用户定义的指令(如宏),该指令与子节点的名称(?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(请参见the table),因此您可以为文本节点定义处理程序,如下所示:

<#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>

请注意,您可以使用_作为<#t>来减少输出中多余的空白量。另请参阅:模板作者指南/其他/空白处理

您可能会说,使用命令式方法进行操作的 FTL 要短得多。没错,但是示例 XML 使用的是非常简单的模式,正如我所说的,声明性方法将其形式带入 XML 模式,这种模式对在哪里出现什么元素不太确定。例如,介绍元素mark,该元素应将文本颜色更改为红色,而不在您使用的位置进行匹配;在titlepara中。为此,使用声明性方法,您只需添加一个宏:

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

然后<mark>...</mark>将自动在任何地方工作。因此,对于某些 XML 模式,声明性 XML 处理实际上将比命令性 XML 处理更短,更重要的是,FTL 更清晰。由您决定何时使用哪种方法。不要忘记,您可以自由地混合使用这两种方法。假设在元素处理程序中,您可以使用命令式方法来处理该元素的内容。