coder
coder

Reputation: 4283

how to skip elements in xslt based on position()

Here is my xslt:

This one works, but only if hardcode '1,2,'

<xsl:template match="row[contains('1,2,',concat(position(),','))]" 

working xslt:

<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="positions"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="row[contains('1,2,',concat(position(),','))]" name="skiprow"/>
</xsl:stylesheet>

But I want to pass the position values as parameter. But it doesn't work. I checked the value of the parameter by adding a line, the parameter is good.

    <xsl:value-of select="$positions"/>
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>

Non-working xslt:

<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="positions"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="row[contains('$positions',concat(position(),','))]" name="skiprow"/>
</xsl:stylesheet>

Sample Xml:

<root>
    <row>
        <column1>7004275</column1>
        <column2>NUVCFDK</column2>
    </row>
    <row>
        <column1>1001459</column1>
        <column2>CAN</column2>
        <column3>12</column3>
        <column4>646.80</column4>
        <column5>23-06-2009</column5>
        <column6>31-12-2009</column6>
        <column7/>
    </row>
    <row>
        <column1>1001461</column1>
        <column2>CAN</column2>
        <column3>1</column3>
        <column4>9.50</column4>
        <column5>23-06-2009</column5>
        <column6>31-12-2009</column6>
        <column7/>
    </row>
</root>

Upvotes: 1

Views: 2281

Answers (1)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

Non-working xslt:

  <xsl:template match="row[contains('$positions',concat(position(),','))]" name="skiprow"/>

In XSLT 1.0 a match pattern is forbidden to contain a variable/parameter reference.

Use instead:

<xsl:template match="row>
  <xsl:if test=
      "not(contains($vPositions, concat(position(),',')))">
   <xsl:call-template name="identity"/>
  <xsl:if>
</xsl:template>

The complete transformation becomes:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="vPositions" select="'1,2,'"/>

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

 <xsl:template match="row">
  <xsl:if test=
   "not(contains($vPositions, concat(position(),',')))">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

and when this transformation is applied on the provided XML document:

<root>
    <row>
        <column1>7004275</column1>
        <column2>NUVCFDK</column2>
    </row>
    <row>
        <column1>1001459</column1>
        <column2>CAN</column2>
        <column3>12</column3>
        <column4>646.80</column4>
        <column5>23-06-2009</column5>
        <column6>31-12-2009</column6>
        <column7/>
    </row>
    <row>
        <column1>1001461</column1>
        <column2>CAN</column2>
        <column3>1</column3>
        <column4>9.50</column4>
        <column5>23-06-2009</column5>
        <column6>31-12-2009</column6>
        <column7/>
    </row>
</root>

the wanted, correct result is produced (rows 1 and 2 "deleted"):

<root>
   <row>
      <column1>1001461</column1>
      <column2>CAN</column2>
      <column3>1</column3>
      <column4>9.50</column4>
      <column5>23-06-2009</column5>
      <column6>31-12-2009</column6>
      <column7/>
   </row>
</root>

Do note, however, that your conditions aren't strong enough -- a parameter value of "11,13," will delete four rows -- 1, 11, 3 and 13.

A good condition to use is:

not(contains($vPositions, concat(',',position(),',')))

That means that the parameter must start and end with the comma character.

Here is the complete, corrected transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="vPositions" select="',1,2,'"/>

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

 <xsl:template match="row">
  <xsl:if test=
   "not(contains($vPositions, concat(',',position(),',')))">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

Upvotes: 3

Related Questions