How to use a parameter in a xslt as a XPath?
Asked Answered
M

2

12

I'd like to add an element to a xml document and I'd like to pass as a parameter the path to the element.

sample.xml file:

<?xml version="1.0"?>
<stuff>
  <element1>
    <foo>2</foo>
<bar/>
  </element1>
  <element2>
<subelement/>
<bar/>
   </element2>
   <element1>
     <foo/>
 <bar/>
   </element1>
 </stuff>

Using:

xalan.exe -p myparam "element1" sample.xml addelement.xslt

I'd like the following result:

<?xml version="1.0"?>
<stuff>
  <element1>
    <foo>2</foo>
    <bar/>
    <addedElement/>
  </element1>
  <element2>
<subelement/>
<bar/>
   </element2>
   <element1>
     <foo/>
 <bar/>
     <addedElement/>
   </element1>
 </stuff>

I've manage to write addelement.xslt, when hardcoding the path it works, but when I try to use parameter myparam in the match attribute I get:

XPathParserException: A node test was expected.
pattern = '$myparam/*[last()]' Remaining tokens are:  ('$' 'myparam' '/' '*' '[' 'last' '(' ')' ']') (addelement.xslt, line 12, column 42)

addelement.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="element1/*[last()]">
    <xsl:copy-of select="."/>
<addedElement></addedElement>
</xsl:template>

</xsl:stylesheet>

addelement.xslt with hardcoded path replaced

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="myparam"/>

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="$myparam/*[last()]">
    <xsl:copy-of select="."/>
<addedElement></addedElement>
</xsl:template>

</xsl:stylesheet>

Thanks for helping

Moravia answered 12/2, 2010 at 9:0 Comment(0)
V
6

I don't think you can use variables/paramaters in matching templates like you have coded. Even this doesn't work

<xsl:template match="*[name()=$myparam]/*[last()]">

Instead, try changing the first matching template to as follows, so that the parameter check is inside the template code, not as part of the match statement.

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <xsl:if test="local-name() = $myparam">
            <addedElement/>
        </xsl:if>
    </xsl:copy>
</xsl:template>
Versicle answered 12/2, 2010 at 9:18 Comment(6)
Do you mean that variable/parameters are not a valid XPath expression ? However, I can't make it work with your solution.Moravia
First of all, XSLT 1.0 does not allow the use of variables/parameters in match patterns at all. XSLT 2.0 does allow it but if you use Xalan, an XSLT 1.0 processor, you can't use XSLT 2.0 features. And secondly, if you have a variable or parameter of type string with an element name then you can't use it to construct XPath expressions with it dynamically. That is not how variable/parameters work, neither in XPath or in other languages. If the variable/parameter value is of type string then you can use it anywhere where a string is allowed, as Tim has done.Fulcrum
Thanks Martin, do you know any command line based XSLT 2.0 processor ?Moravia
Saxon 9 (saxon.sourceforge.net), Gestalt (gestalt.sourceforge.net), AltovaXML Tools (altova.com/altovaxml.html) can all be used from the command line.Fulcrum
I'm confused, I can make it work with Tim's solution both with Xalan and Saxon 9. With Xalan, it was not working because I had to use single quotes around the value of the param in the command line. Since Tim's solution works also into Xalan, I conclude that Tim's solution is XSLT 1.0 compatible. By the way, I am not sure that I understand your first comment. Correct me if I'm wrong: XSLT 2.0 allows variable/parameters in match patterns but they can't be use to construct XPath expression ? Then, how can they be used in match patterns ?Moravia
Tim has posted two snippets, the first one uses $myparam in a match pattern, that is not allowed in XSLT 1.0 and thus any XSLT 1.0 processor should complain about such code. Whether Xalan does complain I have not tested. That snippet is however fine as XSLT 2.0 code as XSLT 2.0 allows using variables/parameters in match patterns. The second snippet that Tim posted is fine as XSLT 1.0 code. As for using a variable of type string in a match pattern or an XPath expression, you can use them anywhere where a string value could be used. Your first attempt, match="$myparam/*[last()]", is not allowed.Fulcrum
F
1

Here is how you could do that with XSLT 1.0:

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

<xsl:param name="n" select="'element1'"/>

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*/*[last()]">
  <xsl:choose>
    <xsl:when test="local-name(..) = $n">
      <xsl:copy-of select="."/>
      <addedElement></addedElement>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
Fulcrum answered 12/2, 2010 at 13:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.