views:

516

answers:

3

I'm trying to set up an XSL:IF statement that only shows entries that have a node that falls between two values. Simple enough, right? It's just a if greater than and if less than. Problem is, instead of testing it against one node I need to test it against up to 52.

Let's say I have some XML that looks like this:

<container>
    <entry>
     <item>1</item>
     <item>2</item>
     <item>3</item>
     <item>4</item>
     <item>5</item>
     <item>6</item>
     <item>7</item>
     <item>8</item>
     <item>9</item>
     <item>10</item>
    </entry>
</container>

Now say I'm given a range of 9–15. Because some of the nodes fall into that range I want to display that entry. But if I was given a range of 11–15 none of the nodes fit so I wouldn't want it displayed.

The problem is... I have absolutely no idea how you would do this. I know you can IF a single value but I can't think of a simple way to test each node.

By the way, this is all being done inside the latest stable release of the Symphony CMS.

[edit] The problem with the first two results is they display the ITEM nodes, what I'm looking for is to return only ENTRY nodes that have at least one ITEM node that matches. I'm not sure how any of the solutions would help this.

+2  A: 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
     <xsl:copy-of select="container/entry/item[number(.) &gt;= 9 and number(.) &lt;= 15]"/>
    </xsl:template>
</xsl:stylesheet>

The XPath statement 'container/entry/item' refers to all matching items. The predicate [number(.) >= 9 and number(.) <= 15] pares that list down. Some XSLT operations (e.g., xsl:value-of) have an implied filter that only grabs the first value. In these cases, you can use xsl:for-each:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
     <xsl:for-each select="container/entry/item[number(.) &gt;= 9 and number(.) &lt; 15]">
      <xsl:value-of select="."/>
     </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
steamer25
@steamer25: That's it, +1. @dougoftheabaci: Whenever you see square brackets in XPath, it is best to think "WHERE" (as in SQL). In this solution the XPath could be translated as "SELECT item FROM container/entry WHERE value >= 9 AND value <= 15". It is a set-based operation, just like an SQL statement.
Tomalak
+1  A: 

How about this.... You can do whatever you want to inside the for-each loop or you can just take the nodeset returned in the variable and use it somewhere else.

   <?xml version='1.0'?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:template match="/">
        <!-- Your first param -->
        <xsl:param name="Param1" select="4"/>
        <!-- Your second param -->
        <xsl:param name="Param2" select="9"/>
            <xsl:variable name="ResultNodeSet">
                <xsl:for-each select="/container/entry/item[number(.) &gt;= $Param1 and number(.) &lt;= $Param2]">
                  <!-- What ever else you want to do can go here-->
                  <xsl:copy-of select="."/>
                </xsl:for-each>
            </xsl:variable>  
        <xsl:value-of select="$ResultNodeSet"/>
    </xsl:template> 
    </xsl:stylesheet>
Jay Stevens
took me 5 edits to get the stupid xslt to display.
Jay Stevens
+1  A: 

You can accomplish this using a nested predicate on the <entry> matches:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <xsl:for-each select="container/entry[item[number(.) &gt;= 9 and number(.) &lt;= 15]]">
            <!-- this will loop over <entry>s which contain <item>s within your range -->
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

The expression would read as "entries which contain items whose values are between 9 and 15".

Ben Blank