3

If I use <xsl:param> without specifying a value, the transformer assumes that the value is an empty string.

In other words, if I forgot to specify a value (e.g. <xsl:param name="N"/>), the compiler doesn't signal an error. This may cause my program to fail silently, which is a bad thing.

How can I specify that my <xsl:param> must have an explicit value? For example, this code should give me an error because there is no explicit value specified:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:call-template name="F1"></xsl:call-template>
        <html>
            <head>
                <title></title>
            </head>
            <body>stuff</body>
        </html>
    </xsl:template>
    <xsl:template name="F1">
        <xsl:param name="N"/> <!-- I Should Get An Error Here! -->
    </xsl:template>
</xsl:stylesheet>

Am looking for a solution in both XSLT 1.0 and XSLT 2.0.

NorvayHais
  • 601
  • 1
  • 7
  • 10
  • Just omit the value assignment in the param declaration. What's the problem? – Emiliano Poggi Jun 30 '11 at 07:55
  • if i omit the value assignment in the param declaration and do not provide a parameter, no errors are thrown. the transformer assumes that i wanted the default value of an empty string and carry on the transformation without throwing any errors. I want to be able to tell the transformer, that this param has no default value, so when i do not explicitly give it an error, the transformer should give me a warning/error instead of carrying on as though it wasn't an error. – NorvayHais Jun 30 '11 at 08:09

5 Answers5

5

In XSLT 2.0, of course, you can say <xsl:param required="yes">, so the problem goes away.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • 1
    Then why did you tag the question xslt-2.0? – Michael Kay Aug 27 '14 at 21:40
  • I tagged it both. In one system XSLT 2.0 is not available. – NorvayHais Aug 29 '14 at 03:38
  • 1
    Well, I answered half your question then. The XSLT 1.0 answer is "you can't" - if a parameter wasn't supplied then it gets a default value and you can't distinguish whether the default value was supplied implicitly or explicitly. – Michael Kay Aug 29 '14 at 10:00
2

You could actually do this with a bit of meta-XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />

  <xsl:template match="xsl:call-template">
    <xsl:variable name="template" select="/xsl:stylesheet/xsl:template[@name=current()/@name]"/>
    <xsl:variable name="call" select="." />
    <xsl:variable name="desc">
      <xsl:value-of select="concat('call to named template &quot;',$template/@name,'&quot; in ')"/>
      <xsl:choose>
        <xsl:when test="ancestor::xsl:template/@name">
          <xsl:value-of select="concat('named template &quot;',ancestor::xsl:template/@name,'&quot;')" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat('template matching &quot;',ancestor::xsl:template/@match,'&quot;')" />
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text>&#10;</xsl:text>
    </xsl:variable>

    <xsl:for-each select="$template/xsl:param[not(@select)]">
      <xsl:if test="not($call/xsl:with-param[@name=current()/@name])">
        <xsl:value-of select="concat('Missing parameter &quot;',@name,'&quot; in ',$desc)" />
      </xsl:if>
    </xsl:for-each>

    <xsl:for-each select="xsl:with-param">
      <xsl:if test="not($template/xsl:with-param[@name=current()/@name])">
        <xsl:value-of select="concat('Unrecognised parameter &quot;',@name,'&quot; in ',$desc)" />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="text()" />
</xsl:stylesheet>

This stylesheet takes any stylesheet as an input, and checks that all call-template's have the right parameters, outputting a message if there's any errors.

This obviously isn't going to put the error checking in the transformer itself, but it will list ALL errors in one go, and can potentially be extended to check for other issues as well.

EDIT: I've adapted it to handle optional parameters, and added in a means of describing where the error is; it's actually a bit of a redesign, with optional parameters simply counting them was going to be tricky, so I removed that bit. Every error is itemized anyway, so the count wasn't really necessary.

Flynn1179
  • 11,925
  • 6
  • 38
  • 74
  • thank you, this idea is wonderful. however the soution is not working. I've included two test cases in my edited question. It's not working for the second test case. second test case is supposed to be an error-free test case yet your solution thinks that it has errors. – NorvayHais Jun 30 '11 at 10:08
  • That's because it didn't account for the possibility of optional parameters; I can fix it. – Flynn1179 Jun 30 '11 at 11:41
  • thanks m8. you've no idea how useful this is! (btw it will be cool if you could update the solution in such a way that it doesn't output the contents, because right now if we take the test input it outputs **stuff** which isn't really required) – NorvayHais Jun 30 '11 at 23:58
  • Woops! Sorry, I only tested it in a simple XSLT with a few call-templates. Just add this template: ``, it'll tell it to ignore any text nodes. – Flynn1179 Jul 01 '11 at 05:15
1

There's a similar option, where you can use a variable that make a choose for the parameter in the called template, for example:

<xsl:template match="/">
<!-- call 1 -->
<xsl:apply-templates select="//encabezado/usuario" mode="forma1">
  <xsl:with-param name="nombre" select="'wwww1'"/>
</xsl:apply-templates>
<!-- call 2 -->
<xsl:apply-templates select="//encabezado/usuario" mode="forma1">
</xsl:apply-templates>

  <xsl:template match="node()" mode="forma1">
<xsl:param name="nombre"/>
<xsl:param name="valor"/>
<xsl:variable name="nombreLocal">
  <xsl:choose>
    <xsl:when test="normalize-space($nombre)">
      <xsl:value-of select="$nombre"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="local-name()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>
<xsl:value-of select ="$nombreLocal"/>
</xsl:template>
1
<xsl:param name="foo" select="false" />
<xsl:if test="not($foo)">
  <xsl:message terminate="yes">You called me with improper params</xsl:message>
</xsl:if>
Boldewyn
  • 81,211
  • 44
  • 156
  • 212
  • this is not working. if i do `` It gives me the error message. But since i've explicitly stated the value of the param, it shouldn't give me any errors. – NorvayHais Jun 30 '11 at 09:37
  • Whats the error? The xslt should terminate with the message "You called me with improper params" – PhillyNJ Oct 14 '13 at 22:01
1

A simple way is checking the input parameter to be not an empty string (specific case mentioned in your comment):

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template name="test">
        <xsl:param name="nodefault"/>
        <xsl:choose>
            <xsl:when test="boolean($nodefault)">
                <xsl:message>do your stuff</xsl:message>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message terminate="yes">Your stuff can't be done</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>


    <xsl:template match="/">
        <xsl:call-template name="test"/>
    </xsl:template>

</xsl:stylesheet>

or much simpler:

<xsl:template name="test">
    <xsl:param name="nodefault"/>
    <xsl:if test="not($nodefault)">
            <xsl:message terminate="yes">Your stuff can't be done</xsl:message>
    </xsl:if>
    <!-- do your stuff -->
</xsl:template>
Emiliano Poggi
  • 24,390
  • 8
  • 55
  • 67
  • just a quick question regarding the code. i've seen that you used `not(boolean($nodefault))` but isn't it true that the `boolean()` is implied and simply `not($nodefault)` will convert `$nodefault` to boolean first before putting the converted boolean through the `not()` function? – NorvayHais Jun 30 '11 at 09:50
  • thanks for the help. but is there a *generic* solution for the problem? I like your idea of setting a value that would be recognized as "wrong". However this will fail if I explicitly call-template with that "wrong-value", since it will throw me an error. That is not what I wanted. If i had supplied a with-param, then regardless of whatever value is in the with-param (even if it is an empty value), there should not be any errors. – NorvayHais Jun 30 '11 at 10:11
  • 1
    @N.Tan: You need to use XSLT 2.0 to stop this XSLT 1.0 masochism. In the incredible case that you can't do this, what you want can be done simply by specifying `NaN` as the value of the `select` attribute of `xsl:param` and testing for this at the start of your called, named template code. I assume that one never intentionally passes a NaN as a parameter value. – Dimitre Novatchev Jun 30 '11 at 12:50