Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT xsl:for-each conditional selection

Tags:

xml

xslt

I have a piece of xml that looks like this:

<root>
  <tag1>tractor</tag1>
  <tag2>
    <subtag1 att="apple" />
    <subtag2>
      <subsubtag1>red</subsubtag1>        
      <subsubtag2>lunch</subsubtag2>
    </subtag2>
  </tag2>      
  <tag1>forklift</tag1>
  <tag2>
    <subtag1 att="pear" />
    <subtag2>
      <subsubtag1>green</subsubtag1>        
      <subsubtag2>breakfast</subsubtag2>
    </subtag2>
  </tag2>      
  <tag2>
    <subtag1 att="apple" />
    <subtag2>
      <subsubtag1>green</subsubtag1>        
      <subsubtag2>dinner</subsubtag2>
    </subtag2>
  </tag2>   
  <tag1>combine harvester</tag1>
</root>

What I need to get transform it so that I get subtags 2 and 3 from each tag2 node, but only the tag2 nodes where subtag1 is apple. I also need a sequence number for each.

My current code looks something like this:

<xsl:for-each select="//tag2">
  <apple>
    <seq_num><xsl:value-of select="position()" /></seq_num>
    <colour><xsl:value_of select="subtag2" /></colour>  
    <meal><xsl:value_of select="subtag3" /></meal>  
  </apple>
</xsl:for-each>

This would be fine, except that I need the for-each only to return the tag2 which are apples (i.e. subtag1 = apple). I can't use an xsl:if or an xsl:when because then the sequence number would be inaccurate for the second apple.

Any ideas?

Thanks, Rik

like image 391
RikSaunderson Avatar asked Jun 15 '11 14:06

RikSaunderson


People also ask

What is for-each condition in terms of xsl?

Introduction of XSLT for each. XSLT for each is defined as the iteration process that allows retrieving data from the specified nodes. Based on the selection criteria defined with these functions, it makes a loop through multiple nodes.

What is xsl for-each select?

The <xsl:for-each> element selects a set of nodes and processes each of them in the same way. It is often used to iterate through a set of nodes or to change the current node. If one or more <xsl:sort> elements appear as the children of this element, sorting occurs before processing.

How do you sum a value in for-each loop in XSLT?

In case of XSLT 1.0, you need to use a recursive template that will keep track of the cumulative value of product ( item_price * gst ) for the repeating <item> node. In case of XSLT 2.0, it is acceptable to use sum(item/(item_price * gst)) expression for computing the sum of the products.

What are the two instructions in xsl that allow you to conditionally process an element based on specific test conditions?

Almost all programming languages have conditional branching structures. XSL has two: <xsl:if> and <xsl:choose>. XPath also has the if-then-else structure.


1 Answers

To stay with your code, should be as simple as:

<xsl:for-each select="//tag2[subtag1/@att='apple']">
  <apple>
    <seq_num><xsl:value-of select="position()" /></seq_num>
    <colour><xsl:value-of select="subtag2/subsubtag1" /></colour>  
    <meal><xsl:value-of select="subtag2/subsubtag2" /></meal>  
  </apple>
</xsl:for-each>

As per the comments below, as suggested, it's better avoid //. You should be able when in the correct context. For instance:

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

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/root">
        <xsl:for-each select="tag2[subtag1/@att='apple']">
            <apple>
                <seq_num><xsl:value-of select="position()" /></seq_num>
                <colour><xsl:value-of select="subtag2/subsubtag1" /></colour>  
                <meal><xsl:value-of select="subtag2/subsubtag2" /></meal>  
            </apple>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Apllied on your input, produces the same result. Or still better, avoid the xsl:for-each as well:

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

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/root">
        <xsl:apply-templates select="tag2[subtag1/@att='apple']"/>
    </xsl:template>

    <xsl:template match="tag2">
        <apple>
            <seq_num><xsl:value-of select="position()" /></seq_num>
            <colour><xsl:value-of select="subtag2/subsubtag1" /></colour>  
            <meal><xsl:value-of select="subtag2/subsubtag2" /></meal>  
        </apple>
    </xsl:template>

</xsl:stylesheet>
like image 60
Emiliano Poggi Avatar answered Oct 02 '22 22:10

Emiliano Poggi