Friday, March 19, 2010

XSL: xsl:element and short tags

I have had problems with XSL and short tags before when using identity transforms, but it can also be an issue doing simple element creation. Obviously as far as a parser is concerned, it makes no difference whatsoever whether you have short tags (e.g. <fish/>) or the complete form (<fish></fish>) for you empty elements, but it makes it much fatter and far less human readable - especially as the .NET System.Xml.Xsl.XslCompiledTransform at least puts closing tags on a new line for empty elements as well as ones with children.

So, if you generate a completely empty element with <xsl:element>, then the short form is used:

<xsl:template match="fish">
 <xsl:element name="{@type}"/>
</xsl:template>

However, if you add any attributes, then the complete form will be used, even though there are still no child elements:

<xsl:template match="fish">
 <xsl:element name="{@type}">
  <xsl:attribute name="type">Fish</xsl:attribute>
 </xsl:element>
</xsl:template>

The trick is to generate your element into an <xsl:variable> and then use <xsl:copy-of> to output it, which results in the short form being maintained:

<xsl:template match="fish">
 <xsl:variable name="el">
  <xsl:element name="{@type}">
   <xsl:attribute name="type">Fish</xsl:attribute>
  </xsl:element>
 </xsl:variable>
 <xsl:copy-of select="$el"/>
</xsl:template>

Easy when you know how. I couldn't find this anywhere on Google, so it's either an obvious thing that everyone but me already knows, my searching sucks, or I'm the first person to have this problem.

Following a comment by Martin Honnen on my original MSDN query about this, it seems that it is a bit more complicated; the basic problem is that XSLT does not define an output format for these situations, so it is entirely up to the processor / serialization combination.

The processor I was using to run these tests was on v2.0 of the framework, and was the deprecated System.Xml.Xsl.XslTransform object, which requires the workaround. The newer System.Xml.Xsl.XslCompiledTransform uses the short form for empty tags and so does not need the workaround.

Annoyingly, the target application uses XslCompiledTransform and therefore would have worked fine, it is only the scratchpad I use for knocking up stylesheets that uses the legacy code that caused the issue! And a quick change of typename for one object and removal of one parameter that was null anyway, and it's bloody working there too. Hurrah for wasted time.

Ah well, if someone is still using the deprecated code then maybe this will help.

No comments:

Post a Comment