学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net
1.基本内容
假设程序员在数据模型中放置了一个XML文档,就是名为 doc
的变量。这个变量和DOM 树的根结点"document"对应。 真实的变量 doc
之后结构是非常复杂的, 大约类似DOM树。所以为了避免钻牛角尖,我们通过例子来看看如何使用。
通过名称来访问元素
这个FTL打印book的title:
<h1>${doc.book.title}</h1>
将会输出:
<h1>Test Book</h1>
正如你所看到的,doc
和 book
都可以当作哈希表来使用。你可以按照子变量的形式来获得它们的子结点。 基本上,你用描述路径的方法来访问在DOM树中的目标(元素title
)。 你也许注意到了上面有一些是假象:使用 ${doc.book.title}
, 就好像我们指示 FreeMarker 打印 title
元素本身, 但是我们应该打印它的子元素文本(看看 DOM 树)。 那也可以办到,因为元素不仅仅是哈希表变量,也是字符串变量。 元素结点的标量是从它的文本子结点级联中获取的字符串结果。然而,如果元素有子元素, 尝试使用一个元素作为标量会引起错误。比如${doc.book}
将会以错误而终止。
该FTL打印2个chapter的title:
<h2>${doc.book.chapter[0].title}</h2> <h2>${doc.book.chapter[1].title}</h2>
这里,book
有两个 chapter
子元素, doc.book.chapter
是存储两个元素结点的序列。 因此,我们可以概括上面的FTL,所以它以任意chapter的数量起作用:
<#list doc.book.chapter as ch> <h2>${ch.title}</h2> </#list>
但是如果只有一个chapter会怎么样呢?实际上, 当你访问一个作为哈希表子变量的元素时,通常 也可以是序列(不仅仅是哈希表和字符串),但如果序列只包含一个项, 那么变量也作为项目自身。所以,回到第一个示例中,它也会打印book的title:
<h1>${doc.book[0].title[0]}</h1>
但是你知道那里就只有一个 book
元素, 而且book也就只有一个title,所以你可以忽略那些 [0]
。 如果book恰好有一个 chapter
(否则它就是模糊的: 它怎么知道你想要的是哪个 chapter
的 title
? 所以它就会以错误而停止),${doc.book.chapter.title}
也可以正常进行。 但是因为一个book可以有很多chapter,你不能使用这种形式。如果元素 book
没有子元素 chapter
, 那么 doc.book.chapter
将是一个长度为零的序列, 所以用FTL <#list ...>
也可以进行。
知道这样一个结果是很重要的,比如,如果 book
没有 chapter
,那么 book.chapter
就是一个空序列,所以 doc.book.chapter??
就 不会 是 false
,它就一直是 true
!类似地,doc.book.somethingTotallyNonsense??
也不会是 false
。来检查是否有子结点,可以使用 doc.book.chapter[0]??
(或doc.book.chapter?size == 0
)。 当然你可以使用类似所有的空值处理操作符 (比如 doc.book.author[0]!"Anonymous"
),只是不要忘了那个 [0]
。
现在我们完成了打印每个chapter所有的 para
示例:
1 <h1>${doc.book.title}</h1> 2 <#list doc.book.chapter as ch> 3 <h2>${ch.title}</h2> 4 <#list ch.para as p> 5 <p>${p} 6 </#list> 7 </#list>
将会输出:
<h1>Test</h1> <h2>Ch1</h2> <p>p1.1 <p>p1.2 <p>p1.3 <h2>Ch2</h2> <p>p2.1 <p>p2.2
上面的FTL可以书写的更加漂亮:
1 <#assign book = doc.book> 2 <h1>${book.title}</h1> 3 <#list book.chapter as ch> 4 <h2>${ch.title}</