1 <page title="Xuriella XSLT Example">
2 <p>Example using XSLT in a Hunchentoot handler to deliver an HTML page</p>
7 Suppose you're writing a web application (say,
8 using <a href="">Hunchentoot</a>), and you would like to show
12 We do that in two steps, strictly separating the programmer-written
13 Lisp code from the XSLT stylesheet that a web designer might want to
19 First we call CL:DIRECTORY, and build a small in-memory XML
20 document listing the files.
23 In the main step, the run that XML document through an XSL
24 stylesheet to generate HTML.
29 <h3>Hunchentoot setup</h3>
31 The example requires hunchentoot and xuriella:
33 <pre style="background-color: #eeeeee; border: 1px solid #cccccc;">
34 (asdf:operate 'asdf:load-op :hunchentoot)
35 (asdf:operate 'asdf:load-op :xuriella)
39 Let's start hunchentoot and register a handler for the example first:
41 <pre style="background-color: #eeeeee; border: 1px solid #cccccc;">
42 (push (tbnl:create-prefix-dispatcher "/show-directory" 'show-directory)
43 tbnl:*dispatch-table*)
44 (tbnl:start-server :port 4242)
47 <h3>Utility functions</h3>
49 Since we might want to write many different handlers using
50 stylesheets, we factor
51 the APPLY-STYLESHEET call out into a convenient macro WITH-STYLESHEET.
52 Its body is expected to provide XML, which it will send through the
53 stylesheet and return the result as a string.
56 Note the use of WITH-XML-OUTPUT and STP:MAKE-BUILDER to build the
57 intermediate XML as an in-memory document using STP.
60 (In real-world code, we could optimize this a little by compiling the
61 stylesheet ahead of time using PARSE-STYLESHEET, and building a cache out
62 of stylesheet objects in a hash table somewhere.)
64 <pre style="background-color: #eeeeee; border: 1px solid #cccccc;">
65 (defmacro with-stylesheet ((stylesheet-pathname) &body body)
66 `(invoke-with-stylesheet (lambda () ,@body) ,stylesheet-pathname))
68 (defun invoke-with-stylesheet (fn stylesheet-pathname)
69 (xuriella:apply-stylesheet (pathname stylesheet-pathname)
70 (cxml:with-xml-output (stp:make-builder)
74 <h3>Building the temporary XML</h3>
76 Now for the handler calling DIRECTORY. We want our XML to look like
79 <directory namestring="/home/jrhacker/">
80 <file>hello-world.lisp</file>
81 <file>mbox</file>
84 which we can generate easily using WITH-ELEMENT and DOLIST:
86 <pre style="background-color: #eeeeee; border: 1px solid #cccccc;">
87 (defun show-directory ()
88 (<b>with-stylesheet</b> ("directory.xsl")
89 (<b>cxml:with-element "directory"</b>
90 (let ((directory (user-homedir-pathname)))
91 (cxml:attribute "namestring" (namestring directory))
92 (<b>dolist (file (directory (merge-pathnames "*.*" directory)))</b>
93 (<b>cxml:with-element "file"</b>
94 (cxml:text (enough-namestring file directory))))))))
97 <h3>An XSL stylesheet as a template</h3>
99 Finally, the XSL stylesheet that turns this into HTML for us. Note
100 the xsl:version on the root element, which marks the literal result
101 element used as a stylesheet.
104 Since <html> is the root element, the stylesheet processor will
105 turn on its HTML output method automatically, and generate HTML 4
106 rather than XML. (Powered by Closure HTML.)
109 To keep the example short and simple, our HTML is not very fancy.
111 <pre style="background-color: #eeeeee; border: 1px solid #cccccc;">
112 <html xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
116 <xsl:value-of select="/directory/@namestring"/>
122 Index of <xsl:value-of select="/directory/@namestring"/>
126 <xsl:for-each select="/directory/file">
128 <xsl:value-of select="."/>
138 That's it. If you open <tt>http://localhost:4242/show-directory</tt>
139 in a browser, you should see a listing of your home directory.