Moving Away From Xslt

20 September 2003

All of this site is written in simple XML documents and transformed to HTML. I find this works really well, and means I never have to worry about dealing with HTML formats. (Not that fancy layout is my style, as you can tell.) I've even written a whole book that way.

For most of this time I've used XSLT as my transformation language. I've got pretty good with slinging XSLT around and getting it to do what I want.

But no more.

When I wrote the software for this Bliki (on a long flight) I did it in Ruby.Prior to that I used Ruby to do a new version of my home page. My conclusion from this exercise was that using Ruby for XML transforms was much easier than using XSLT.

  • XML makes a lousy syntax for a programming language. There's way too much noise in there and as a result you can't see the program.
  • XSLT makes calling subroutines so painful that you are seriously discouraged from using them, which encourages duplicate code.
  • XSLT handles simple tasks well, but is baroque when it comes to more complicated things. Indeed some are impossible and you have to jump out into another language anyway.
  • Ruby gives me a clean, OO language with clear syntax and a kick-ass XML library.(Python may well be just as good, I haven't tried it.)
  • I can mix template style code with transformer style code.

The design of XSLT does influence the way I use the program. My basic transform tasks are handled using recursive apply functions just like XSLT's apply-template command. In this case I use ruby's reflection to make it work well. The guts of the transformer is

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

Essentially the handle_element method uses reflection to invoke a properly named function in the handler object. I subclass the ElementHandler for specific kinds of page. So I have a question tag for the questions that appear on certain pages. For this I write a short transformation routine.

  def handle_question anElement
    @html.p {@html.element('b'){apply anElement}}
  end

The @html.element method pumps out b tags into the output. Inside those b tags are the result of executing the block of code {apply anElement} which continues the recursion. Here ruby's blocks come in very useful.

There are probably tons of ways to improve on this, but I haven't needed to touch the code much since I made the bliki live. Where I have I've found it much easier to deal with than the XSLT.

I think this may raise some real questions about XSLT. There's still much I like about the power of XSLT, but I hate the syntax and the walls you keep running into. I'm not going to convert my whole site over to Ruby tomorrow - most of the XSLT works fine - but I can certainly see my way to doing that at some point in the future. But the bigger question is whether you're better off with scripting language for this kind of task than XSLT.