tags:

views:

212

answers:

2

In XSLT 2.0, is there an easy way to replace named placeholders in a string?

I'm thinking of something like Python's string.Template, where you can do this:

d = dict(name='Joe', age='50')
print Template("My name is $name and my age is $age").substitute(d)

The point is to externalize the string, so it can be easily changed. The only way I've found so far is to use a named xsl:template with parameters, but this is pretty verbose. Is there an easier way?

+1  A: 

There isn't a high-level feature on the order of Python string templates, but you could do something similar using xsl:analyze-string, which will let you process a regex analysis of a string a piece at a time. If you wanted the replacments to be table-driven you could set up an xsl:key to store the mapping, and write an xsl:function to perform the replacement on an arbitrary string.

Not the easiest thing in the world, but certainly doable and reusable later if done correctly.

Jim Garrison
Good idea to use analyze-string -- see my solution, posted as a separate answer.
JW
+1  A: 

I ended up doing something like this:

<!-- Reusable template to perform substitutions on a string -->
<xsl:template name="substitutions">
    <!-- "string" is a string with placeholders surrounded by {} -->
    <xsl:param name="string" />
    <!-- "subs" is a list of nodes whose "key" attributes are the placeholders -->
    <xsl:param name="subs" />
    <xsl:analyze-string select="$string" regex="\{{(.*?)\}}">
     <xsl:matching-substring>
      <xsl:value-of select="$subs/sub[@key=regex-group(1)]" />
     </xsl:matching-substring>
     <xsl:non-matching-substring>
      <xsl:value-of select="." />
     </xsl:non-matching-substring>
    </xsl:analyze-string>
</xsl:template>

<!-- Example use of template -->
<xsl:variable name="nameStr">My name is {name} and my age is {age}</xsl:variable>
<xsl:call-template name="substitutions">
    <xsl:with-param name="string" select="$nameStr" />
    <xsl:with-param name="subs">
     <sub key="name">Joe</sub>
     <sub key="age">50</sub>
    </xsl:with-param>
</xsl:call-template>

I had to use attributes for the substitution names, rather than just passing nodes with different names (e.g. <name>Joe</name>). XPath (or at least Saxon, the processor I'm using) doesn't seem to allow dynamic expressions like "$subs/regex-group(1)". But it does allow "$subs/sub[@key=regex-group(1)]".

JW