Contents
揭示XML文档
test.xml
1 | <book> |
- 根节点是“文档”而不是book
- 如果 B 是 A 的直接后继,我们说 B 节点是 A 节点的 child 子节点,A节点是B节点的parent父节点。
- 元素,文本,注释,处理指令都是DOM树的节点
将XML放到数据模型中
1 | Map root = new HashMap(); |
- parse 方法默认移除注释和处理指令节点。参见 API 文档获取详细信息。
- NodeModel 也允许你直接包装 org.w3c.dom.Node。首先你也许想用静态的实用方法清空 DOM 树,比如 NodeModel.simplify 或你自定义的清空规则。
必要的XML处理
通过例子来学习
1 | <h1>${doc.book.title}</h1> |
尝试访问有子元素的元素将导致错误
1 | <h1>${doc.book}</h1> |
doc.book.chapter是存储两个元素节点的序列
1 | <h2>${doc.book.chapter[0].title}</h2> |
就算只有一个元素节点,也可以当做一个序列
1 | <h1>${doc.book[0].title[0]}</h1> |
如果 book 没有 chapter,那么book.chapter 就是一个空序列,所以 doc.book.chapter 就不会是 false,它就一直是 true!类似地,<#if doc.book.somethingTotallyNonsense??></#if>
也不会是false。来检查是否有子节点,可以使用doc.book.chapter[0]??(或doc.book.chapter?size == 0)。当然你可以使用类似所有的控制处理操作符(比如 doc.book.author[0]!”Anonymous”),只是不要忘了那个[0]。
一个完整示例
1 | <#assign book = doc.book> |
得到所有的para
1 | <#list doc.book.chapter.para as p> |
访问属性
如果test.xml中title是属性而不是元素,只需在其前加@即可。
1 | <#assign book = doc.book> |
如果你很好奇是否 foo 含有属性 bar,那么你不得不写 foo.@bar[0]??来验证。( foo.@bar??是不对的,因为它总是返回 true)。类似地,如果你想要一个bar属性的默认值,那么你就不得不写 foo.@bar[0]!”theDefaultValue”。
探索DOM
枚举所有book的子元素
1 | <#list doc.book?children as c> |
children:子元素序列
node_type:节点类型,”element”, “text”, “comment”, “pi”
node_name:节点名称
1 | <book foo="Foo" bar="Bar" baaz="Baaz"> |
可以通过元素的自变量@@获取元素的属性序列
1 | <#list doc.book.@@ as attr> |
返回元素的子节点序列可以用*
1 | <#list doc.book.* as c> |
可以使用内建函数 parent 来获得元素的父节点。你可以使用内建函数 root 来快速返回到文档节点
1 | <#assign e = doc.book.chapter[0].para[0]> |
使用XPath表达式
XPath 表达式仅在 Jaxen(推荐使用,但是使用至少 Jaxen 1.1-beta-8 版本,不能再老了) 或 Apache Xalan 库可用时有效。
1 | <#list doc["book/chapter[title='Ch1']/para"] as p> |
注意 XPath 序列的项索引从 1 开始,而 FTL 的序列项索引是用 0 开始的。
如果使用 Jaxen 而不是 Xalan,那么 FreeMarker 的变量在使用 XPath 变量引用时是可见的
1 | <#assign currentTitle = "Ch1"> |
注意$currentTitle 不是 FreeMarker 的插值,因为那里没有{和}。那是 XPath 表达式。
一些 XPath 表达式的结果不是节点集,而是字符串,数字或者布尔值。获取para元素的总数
1 | ${x["count(//para)"]} |
XML命名空间
如果元素book是命名空间 http://example.com/ebook,那么你不得不关联一个前缀,要在模板的顶部使用 ftl 指令的 the ns_prefixes 参数:
1 | < |
现在你可以编写如 doc[“e:book”]的表达式。使用保留前缀D可以设置默认命名空间。XPath不支持默认命名空间
1 | < |
注意当你使用默认命名空间时,那么你可以使用保留前缀 N 来选择不属于任意节点空间的元素。比如 doc.book[“N:foo”]。这对 XPath 表达式不起作用
转义
1 | <#escape x as x?html> |
声明的XML处理
最经常使用来处理声明方式的指令就是 recurse 指令,这个指令获取节点变量,并把它作为是参数,从第一个子元素开始,一个接一个地“访问”所有它的子元素。“访问”一个节点意味着它调用了用户自定义的指令(比如宏),它的名字和子节点( ?node_name)的名字相同。我们这么说,用户自定义指令操作节点。使用用户自定义指令处理的节点作为特殊变量.node 是可用的。
1 | < |
如果你调用 recurse 而不用参数,那么它使用.node,也就是说,它访问现在处理这个节点的所有子节点。
所有文本节点的节点名字都是@text。转义HTML
1 | <#macro @text>${.node?html}</#macro> |
默认处理器
- 文本节点: 打印其中的文本。要注意,在很多应用程序中,这对你来说并不好,因为你应该在你发送它们到输出(使用?html 或?xml 或?rtf 等,这基于输出的格式)前转义这些文本。
- 处理指令节点: 如果你定义了自定义指令,可以通过调用处理器调用@pi,否则将什么都不做(忽略这些节点)。
- 注释节点,文档类型节点:什么都不做(忽略这些节点)。
- 文档节点:调用 recurse,也就是说,访问文档节点的所有子节点。
元素节点的情形,这意味着如果你定义了一个称为@element 的宏(或其他种类的用户自定义指令),没有其他特定的处理器时,那么它会捕捉所有元素节点。如果你没有@element 处理器,那么你必须为所有可能的元素定义处理器。
属性节点在 recurse 指令中不可见,所以不需要为它们编写处理器。
访问单独节点
使用visit指令你可以访问单独的节点
XML 命名空间
1 | <book xmlns="http://example.com/ebook"> |
考虑命名空间
1 | <#ftl ns_prefixes={"e":"http://example.com/ebook"}> |
设置为默认命名空间
1 | <#ftl ns_prefixes={"D":"http://example.com/ebook"}> |