This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: Parameter substitution
- From: Jeni Tennison <jeni at jenitennison dot com>
- To: Curtis Burisch <burisch at clara dot co dot uk>
- Cc: xsl-list at lists dot mulberrytech dot com
- Date: Wed, 19 Dec 2001 18:10:10 +0000
- Subject: Re: [xsl] Parameter substitution
- Organization: Jeni Tennison Consulting Ltd
- References: <E16GkZ4-000MFR-00@oceanus.uk.clara.net>
- Reply-to: xsl-list at lists dot mulberrytech dot com
Hi Curtis,
> Below is given a fragment from an XML document. Of note are the
> contents of the <ErrorText> nodes; the first one has a value of "%1
> %2 is not a valid date format.".
>
> My task, (you guessed it) is to replace each %1, %2 etc.. with a
> string built from the correspondong ErrorParameter (formatted nicely
> of course) . Being relatively new to XSLT, I can't think of a way to
> accomplish this in a generic manner. Has anyone got any ideas? I
> can't think of an approach that will actually work!
It's a little tedious because XPath string-handling isn't the best
(roll on regexps), but it's certainly possible. To work through the
string, you need a recursive template; it needs to take the string in
which you're replacing parameter references, and a list of parameters
that you want to replace the references with:
<xsl:template name="substituteParameters">
<xsl:param name="string" />
<xsl:param name="parameters" select="/.." />
...
</xsl:template>
You need to work through the string a bit at a time. If the string
doesn't contain any % characters, then you know that you can just
output the string with no changes, so that's the basic test on which
the template hinges:
<xsl:template name="substituteParameters">
<xsl:param name="string" />
<xsl:param name="parameters" select="/.." />
<xsl:choose>
<xsl:when test="contains($string, '%')">
...
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Now, given that you have a string with a % in it, you want to output:
- the string up to the %
- the parameter in the position indicated by the number after the %
- the result of calling this same template on the rest of the string
(after the number)
The first bit's easy - you can use the substring-before() function to
get the bit before the %:
<xsl:template name="substituteParameters">
<xsl:param name="string" />
<xsl:param name="parameters" select="/.." />
<xsl:choose>
<xsl:when test="contains($string, '%')">
<xsl:value-of select="substring-before($string, '%')" />
...
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The second bit's a little more complicated - you need to get the
number after the %. If you can guarantee that there aren't going to be
more than 9 parameters, then you only need to get the first character
after the %, so I'll assume that's the case (let me know if it's not).
So you can use the following to get the first character after the %:
substring(substring-after($string, '%'), 1, 1)
That gives you (a string representation of) a number, which you can
use to index into the parameters passed as the value of the
$parameters parameter:
$parameters[position() =
substring(substring-after($string, '%'), 1, 1)]
or:
$parameters[number(substring(substring-after($string, '%'), 1, 1))]
I'd apply templates to this parameter - you can have a template
matching ErrorParameter elements that does the relevant pretty
formatting:
<xsl:template name="substituteParameters">
<xsl:param name="string" />
<xsl:param name="parameters" select="/.." />
<xsl:choose>
<xsl:when test="contains($string, '%')">
<xsl:value-of select="substring-before($string, '%')" />
<xsl:apply-templates
select="$parameters[number(substring(
substring-after($string, '%'),
1, 1))]" />
...
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Finally, the recursive call to the template needs to pass in the same
set of parameters whilst amending the string to being the 'rest' of
the substring after the '%':
<xsl:template name="substituteParameters">
<xsl:param name="string" />
<xsl:param name="parameters" select="/.." />
<xsl:choose>
<xsl:when test="contains($string, '%')">
<xsl:value-of select="substring-before($string, '%')" />
<xsl:apply-templates
select="$parameters[number(substring(
substring-after($string, '%'),
1, 1))]" />
<xsl:call-template name="substituteParameters">
<xsl:with-param name="string"
select="substring(substring-after($string, '%'), 2)" />
<xsl:with-param name="parameters" select="$parameters" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
To call it, you'd do something like the following (assuming that
SynchError is the current node):
<xsl:call-template name="substituteParameters">
<xsl:with-param name="string" select="ErrorText" />
<xsl:with-param name="parameters" select="ErrorParameter" />
</xsl:call-template>
Having said all of that, you would be a lot better off if you could
change the XML structure so that you used elements to indicate where
the parameters should be inserted. Something like:
<ErrorText>
<Insert param="1" /> <Insert param="2" /> is not a valid date
format.
</ErrorText>
That way, you could just apply templates to the content of the
ErrorText element and use templates matching Insert elements to insert
the value of the relevant parameter.
I hope that helps,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list