 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

October 29th, 2010, 10:23 AM
|
|
Authorized User
|
|
Join Date: Sep 2004
Posts: 67
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Filtering results
Okay, I have a problem I've been trying to solve myself for a while now and it's starting to do my head in... Any help is greatly appreciated.
I have some XML structured like this:
Code:
<Item>
<Id>5326</Id>
<Title>Some title text</Title>
<Teaser>Some teaser text</Teaser>
<SubGenre>Europe</SubGenre>
<Image>9052740.jpg</Image>
</Item>
Now I have many Items, all of which contain a different SubGenre. What I'm trying to do in XSL is filter based on SubGenre (so that I can pull in just the Item's which contain SubGenre='Europe', for example) and then loop through all of those using xsl:for-each however, so far I just cannot get it to work.
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:if test="Item[SubGenre='Europe']">
<div class="Genre">
<div class="GenreHdr"><img src="europe.gif" alt="Europe" width="90" height="10"/></div>
<div class="GenreC1">
<ul>
<xsl:for-each select=".[position() < 16]">
<li>Some text here</li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Any suggestions are greatly appreciated!
-
|
|

October 29th, 2010, 10:37 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Code:
<xsl:template match="/">
<xsl:for-each select="//Item[SubGenre = 'Europe']">
...
</xsl:for-each>
</xsl:template>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|

October 29th, 2010, 10:44 AM
|
|
Authorized User
|
|
Join Date: Sep 2004
Posts: 67
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Quote:
Originally Posted by Martin Honnen
Code:
<xsl:template match="/">
<xsl:for-each select="//Item[SubGenre = 'Europe']">
...
</xsl:for-each>
</xsl:template>
|
Thanks, Martin. Is there a way to first test for 'Europe' before going into a for-each? The reason I want to test for it first is so that if no 'Europe' match is found the div container isn't rendered.
I also want to jump into the for-each after the header div so that the title gif isn't looped over and over.
Is that possible? Cheers!
|
|

October 29th, 2010, 10:48 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Consider to post a sample of the XML input and the corresponding output you want to create, currently I don't understand what your problem is. Of course you can use xsl:if test="//Item[SubGenre = 'Europe']".
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|

October 29th, 2010, 11:03 AM
|
|
Authorized User
|
|
Join Date: Sep 2004
Posts: 67
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
I don't appear to get any output. I get the following error message:
Code:
General Exception Error: Abbreviated step '.' cannot be followed by a predicate. Use the full form 'self::node()[predicate]' instead. . -->[<-- position() < 16]
So I changed my for-each to...
Code:
<xsl:for-each select="self::node()[position() < 16]">
But all that renders is the div's, but they're empty. Perhaps it's got something to do with not being in the right context node? I'm not sure.
I also tried nested xsl:for-each, rather than xsl:if first followed by xsl:for-each second.
|
|

October 29th, 2010, 11:32 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
>Abbreviated step '.' cannot be followed by a predicate. Use the full form 'self::node()[predicate]' instead. . -->[<-- position() < 16]
You can write .[position() < 16] in XSLT 2.0 but not in XSLT 1.0. But even in 2.0, it doesn't do what you seem to be imagining. "." selects the context item, X[] selects a subset of X, so X[position()<16] selects the first 16 items of X, and if X is a single item like ".", then it selects that item alone. Which is not much use to you.
If you're trying to output the first 15 matches, then do <xsl:for-each select="//Item[SubGenre='XYZ'][position() < 16 ]">...</xsl:for-each>
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

October 29th, 2010, 11:35 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
As I said, consider to post a sample of the XML input and the corresponding output you want to create, then we can suggest a solution with XSLT.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|

October 29th, 2010, 11:42 AM
|
|
Authorized User
|
|
Join Date: Sep 2004
Posts: 67
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Quote:
Originally Posted by mhkay
>Abbreviated step '.' cannot be followed by a predicate. Use the full form 'self::node()[predicate]' instead. . -->[<-- position() < 16]
You can write .[position() < 16] in XSLT 2.0 but not in XSLT 1.0. But even in 2.0, it doesn't do what you seem to be imagining. "." selects the context item, X[] selects a subset of X, so X[position()<16] selects the first 16 items of X, and if X is a single item like ".", then it selects that item alone. Which is not much use to you.
If you're trying to output the first 15 matches, then do <xsl:for-each select="//Item[SubGenre='XYZ'][position() < 16 ]">...</xsl:for-each>
|
Thanks! That worked.
Can I push my luck and ask whether there is a way to first check for existance of a particular sub-genre before the xsl:for-each block?
Issue I have is that if XYZ doesn't exist for example, the HTML within the for-each block isn't rendered as one would expect, but the title gif image above it still is. I can't put the gif inside the for-each block otherwise it'll loop, but I'd like to be able to check that there is at least one record match before XSL outputs the gif otherwise the result shown to user is a blank container. (Hope that makes sense!)
|
|

October 29th, 2010, 09:09 PM
|
|
Authorized User
|
|
Join Date: Sep 2004
Posts: 67
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Quote:
Originally Posted by Martin Honnen
As I said, consider to post a sample of the XML input and the corresponding output you want to create, then we can suggest a solution with XSLT.
|
Hi Martin,
Hopefully I can explain this correctly... Here is the XML input...
Code:
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item>
<Id>5326</Id>
<Title>Some title text</Title>
<Teaser>Some teaser text</Teaser>
<SubGenre>Europe</SubGenre>
<Image>9052740.jpg</Image>
</Item>
<Item>
<Id>5327</Id>
<Title>Some title text</Title>
<Teaser>Some teaser text</Teaser>
<SubGenre>Asia</SubGenre>
<Image>9052741.jpg</Image>
</Item>
<Item>
<Id>5328</Id>
<Title>Some title text</Title>
<Teaser>Some teaser text</Teaser>
<SubGenre>Americas</SubGenre>
<Image>9052742.jpg</Image>
</Item>
<Item>
<Id>5329</Id>
<Title>Some title text</Title>
<Teaser>Some teaser text</Teaser>
<SubGenre>Europe</SubGenre>
<Image>9052743.jpg</Image>
</Item>
</Items>
Now what I want to be able to do is query all Item where SubGenre corresponds to 'Europe' and output as follows. I have included red comments (not actually in my XSL document) to help you see what I'm trying to achieve.
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<!-- Check to see if SubGenre matching Europe exists before rendering div container -->
<xsl:if test="Items/Item[SubGenre = 'Europe']">
<div class="Genre">
<div class="GenreHdr"><img src="europe.gif" alt="Europe" width="42" height="16"/></div>
<div class="GenreC1">
<ul>
<!-- Loop through each SubGenre matching Europe where position less than 4 -->
<xsl:for-each select="Items/Item[SubGenre = 'Europe'][position() < 4]">
<li><xsl:value-of select="Title" /> - <xsl:value-of select="Teaser" disable-output-escaping="yes" /></li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The issue I have is that if I don't test for SubGenre containing Europe before getting to the for-each, the output will still render the div containers and just omits the <li>XX</li> part within the for-each if there is no SubGenre containing 'Europe'
Code:
<div class="Genre">
<div class="GenreHdr"><img src="europe.gif" alt="Europe" width="42" height="16"/></div>
<div class="GenreC1">
<ul>
</ul>
</div>
</div>
If there are no SubGenre containing Europe I would not want XSL to output the div's either as this displays a gif header to the user with no associated data to display, but I can't put the div / img tags within the for-each otherwise XSL renders the title gif over and over for each SubGenre matching 'Europe'.
Hope that makes sense?
Last edited by spinout; October 29th, 2010 at 09:19 PM..
|
|

October 30th, 2010, 07:23 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Does the latest XSLT sample you posted not achieve what you want? The xsl:if test="Items/Item[SubGenre = 'Europe']" looks fine for the input sample you posted.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|
 |