告别 XSLT
2003 年 9 月 20 日
整个网站都是用简单的 XML 文档编写,并转换为 HTML。我发现这种方法非常有效,而且意味着我永远不必担心处理 HTML 格式。(当然,你也可以看出,我并不喜欢花哨的布局。)我甚至用这种方法写了一本整本书。
在大部分时间里,我一直使用 XSLT 作为我的转换语言。我对 XSLT 非常熟悉,并且能够让它按照我的意愿工作。
但现在不再了。
当我编写这个 Bliki 的软件(在一次长途飞行中)时,我用 Ruby 完成了。在此之前,我使用 Ruby 做了一个新版本的我的主页。从这次练习中,我得出的结论是,使用 Ruby 进行 XML 转换比使用 XSLT 要容易得多。
- XML 作为编程语言的语法很糟糕。里面有太多噪音,因此你无法看到程序。
- XSLT 使调用子例程变得非常痛苦,以至于你严重地不想使用它们,这会导致代码重复。
- XSLT 在处理简单任务时表现良好,但在处理更复杂的任务时却很繁琐。事实上,有些任务是不可能完成的,你必须跳到另一种语言中才能完成。
- Ruby 为我提供了一种干净的、面向对象的语言,它具有清晰的语法和一个很棒的 XML 库。(Python 可能也一样好,但我还没有尝试过。)
- 我可以将模板风格的代码与转换器风格的代码混合在一起。
XSLT 的设计确实影响了我使用程序的方式。我的基本转换任务是使用递归应用函数来处理的,就像 XSLT 的 apply-template 命令一样。在这种情况下,我使用 ruby 的反射来使其正常工作。转换器的核心是
class ElementHandler def apply anElement anElement.each {|e| handle(e)} if anElement end def handle aNode if aNode.kind_of? REXML::Text handleTextNode(aNode) elsif aNode.kind_of? REXML::Element handle_element aNode else return #ignore comments and processing instructions end end def handle_element anElement handler_method = "handle_" + anElement.name.tr("-","_") if self.respond_to? handler_method self.send(handler_method, anElement) else default_handler(anElement) end end ...
本质上,handle_element
方法使用反射在处理程序对象中调用一个命名正确的函数。我为特定类型的页面对 ElementHandler 进行子类化。因此,我有一个 question 标签,用于显示在某些页面上的问题。为此,我编写了一个简短的转换例程。
def handle_question anElement @html.p {@html.element('b'){apply anElement}} end
@html.element
方法将 b 标签输出到输出中。在这些 b 标签内是执行代码块 {apply anElement}
的结果,该代码块继续递归。在这里,ruby 的代码块非常有用。
可能有很多方法可以改进它,但我自从让 bliki 上线后,就很少需要修改代码。在我修改代码的地方,我发现它比 XSLT 容易处理得多。
我认为这可能会对 XSLT 提出一些真正的问题。我仍然非常喜欢 XSLT 的强大功能,但我讨厌它的语法以及你不断遇到的障碍。我不会明天就把整个网站都转换为 Ruby——大部分 XSLT 工作得很好——但我确实可以看到在将来的某个时候这样做。但更大的问题是,对于这种类型的任务,你是否应该使用脚本语言而不是 XSLT。