so

Text value templates

Volume 7, Issue 5; 10 Jan 2023

TIL: Be careful with text value templates in mixed content.

In XSLT, text value templates can save a few keystrokes and, in some contexts, make your code a lot clearer. My XSLT coding habits predate them and I don’t tend to use them very often.

If you do use them, it’s worth remember how they interact with whitespace stripping. What happened was, I was looking at some HTML and I thought the text decoration of a link was a bit weird. When I looked at the source, I was very surprised by the whitespace.

Here’s a simple example to illustrate what happened. Consider some XML:

<p>
  <pubdate>2023-01-10</pubdate>
</p>

Suppose you want to transform that pubdate into something more human readable. You decide to use fn:format-date:

<xsl:template match="pubdate">
  <time datetime="{.}">
    <xsl:sequence select="format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]')"/>
  </time>
</xsl:template>

All well and good, that transformation produces:

<p>
  <time datetime="2023-01-10">10 Jan 2023</time>
</p>

Suppose on another day, you decide to do it with a text value template to save yourself a few keystrokes:

<xsl:template match="pubdate" expand-text="yes">
  <time datetime="{.}">
    { format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]') }
  </time>
</xsl:template>

Same thing, right? Not quite. The output now looks like this:

<p>
  <time datetime="2023-01-10">
    10 Jan 2023
  </time>
</p>

Because the content of the time element contained non-whitespace characters, whitespace stripping didn’t apply. That means the whitespace I added to make the code easier to read wound up in the output. This probably doesn’t matter much, especially in HTML, except that if the extra whitespace is inside a link, the text decoration (like underlining) that applies to the link also applies to the extra whitespace!

You could mitigate this by writing

<xsl:template match="pubdate" expand-text="yes">
  <time datetime="{.}">{
    format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]')
  }</time>
</xsl:template>

or even

<xsl:template match="pubdate" expand-text="yes">
  <time datetime="{.}"
    >{ format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]') }</time>
</xsl:template>

But those are just awful.

Maybe this is why I don’t use text value templates very much.

#TIL #XSLT