XSLT 2.0 in production
Or, “what I did on my winter vacation.”
Sun is closed in the last week of December and one of my goals for this vacation was converting all the stylesheets that generate content for this site to XSLT 2.0. (I had a bunch of other goals too: switching my laptop to Solaris, getting some new DocBook releases out, upgrading the server in the closet to Ubuntu, etc., but this one I actually achieved.)
Anticlimactically, if I was successful, you can't tell. It took me a few days, mostly because in any code-review you find cruft you can remove or functions that you can improve. Every experience that I have with XSLT 2.0 increases my enthusiasm for it. So many things are made so much easier by the introduction of sequences, user-defined functions, and direct access to result trees.
The most tentative decision I made was to abandon RDF Twig and return to processing RDF/XML directly with XSLT. I'm relying on the output of cwm to produce a single XML document containing all my RDF encoded very regularly. Then with a couple of keys and a variable binding to hold the RDF, this function makes access to parts of the graph straightforward:
<xsl:function name="f:get-rdf" as="element()?">
<xsl:param name="about"/>
<xsl:choose>
<xsl:when test="empty($about)">
<xsl:sequence select="$about"/>
</xsl:when>
<xsl:when test="$about instance of element()">
<xsl:choose>
<xsl:when test="$about/@rdf:nodeID">
<xsl:variable name="node"
select="key('nodeID', $about/@rdf:nodeID, $allrdf)"/>
<xsl:choose>
<xsl:when test="$node">
<xsl:sequence select="$node"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$about"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$about/@rdf:resource">
<xsl:variable name="node"
select="key('about', $about/@rdf:resource, $allrdf)"/>
<xsl:choose>
<xsl:when test="$node">
<xsl:sequence select="$node"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$about"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$about"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="key('about',string($about), $allrdf)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
The way it works is that if you pass it an element with an rdf:nodeID
or rdf:resource
attribute, it returns the node in the
graph with that identifier. If you pass it a string, URI,
attribute, or something else, it treats it like a URI and finds the
node in the graph identified by that URI. What that means in practice
is that if you've got a property or value in your hand, you can pass it
to f:get-rdf
and you'll get back the
rdf:Description
element in the graph for the right node.
Handy.
I've still got a little bit of tidying to do around the edges, most especially in the area of trip itineraries which aren't handling the fact that my RDF PIM stores all the times in UTC very well. Oh, and all the XSL FO used for PDF output is still generated with XSLT 1.0.
If you notice anything wrong, it's probably a bug, so please let me know.
And Happy New Year!
Comments
Bruce pointed out that I had broken comments. Have I fixed them? I think so. (But I just noticed that the previous/next links are broken; something to fix tomorrow.)
Yup, it works again now. My question was:
Have you considered just using SPARQL and piping the returned XML results to the XSLT processor from there?
I need to figure out how to adapt citeproc to RDF, and was thinking about doing something like this.
Also, I take it you use a key to index all the rdf:Description nodes?
Bruce, I have a couple of keys for the rdf:Description elements. With respect to SPARQL, there isn't really any single query that would give me the information I want. If I reimplemented rdftwig, I'd probably provide a mechanism to submit SPARQL queries to it, though.