Here is a problem: XSLT 1.0 sucks on generating XML character or entity references. I mean getting &foo; out of XSLT 1.0 is hard. The only ugly solution is disable-output-escaping hack, but it's a) optional, b)doesn't work in all scenarios (only when XSLT engine controls output serialization into bytes and c) works only on text nodes. Latter is real showstopper - you can't generate character or entity reference in attribute using XSLT 1.0. But now that we have XSLT 2.0, which is oh so better. What's XSLT 2.0 solution for the problem?
In XSLT 2.0 disable-output-escaping is deprecated. New facility for the problem is called Character Maps. Character maps are simpler, better and don't mess with data model. Formal definition:
A character map allows a specific character appearing in a text or attribute node in the final result tree to be substituted by a specified string of characters during serialization. The effect of character maps is defined in [XSLT and XQuery Serialization].
This is basically declarative replace for XSLT output.
Let's say you want to generate this XUL fragment (real world question):
<menuitem label="&context.add.building;"/>
XSLT 2.0 solution would be to define a character map, which maps ampersand character to ugh... ampersand character :) For this to make sense you should realize that mapping a character effectively disables its XML or HTML escaping. It's like you say "I want this character to be outputted as this string and don't mess with it you, the engine". That's powerful enough even to disable escaping of reserved characters such as & and <.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:output use-character-maps="xul" /> <xsl:character-map name="xul"> <xsl:output-character character="&" string='&'/> </xsl:character-map> <xsl:template match="contextMenu"> <menuitem label="&context.add.{name()}"/> </xsl:template> </xsl:stylesheet>
Awesome. I can't wait, I want this in XSLT 1.0 too. I think I'm going to implement character maps for nxslt.exe tool. The only problem is how to provide mapping info. I'll post about it next time.
[Tested with Saxon 8.8B for .NET].
Leave a comment