[xsl] Excluding too much in nested for-each

Subject: [xsl] Excluding too much in nested for-each
From: "Ragulf Pickaxe" <jawxml@xxxxxxxxxxx>
Date: Wed, 21 Apr 2004 06:00:44 +0000
Hi all,

This post is a little long, but I have cut it down to the least that can also give a description of the problem (I hope).
I hope that some of you have the time to look at this, and that it is understandable.


I had a question a coupple of years ago, and have only now found a bug in the solution - problem is that I don't know how to correct it. I have tried to find the original posts in the archieves, but not been able to do so.

I have an XML that is flat, which I want to output in a tree view:

<!-- Simplified -->
<root>
<Elements>
 <Elem Date="01-01-2003" Type="1"> <!-- A -->
   <ElemID>10</ElemID>
   <Name>A</Name>
 </Elem>
 <Elem Date="01-01-2003" Type="2"> <!-- B -->
   <ElemID>10</ElemID>
   <Name>B</Name>
 </Elem>
 <Elem Date="01-01-2003" Type="2"> <!-- C -->
   <ElemID>10</ElemID>
   <Name>C</Name>
 </Elem>
 <Elem Date="01-04-2003" Type="2"> <!-- D -->
   <ElemID>10</ElemID>
   <Name>D</Name>
 </Elem>
 <Elem Date="01-04-2003" Type="2"> <!-- E -->
   <ElemID>13</ElemID>
   <Name>E</Name>
 </Elem>
 <Elem Date="01-08-2003" Type="1"> <!-- F -->
   <ElemID>12</ElemID>
   <Name>F</Name>
 </Elem>
 <Elem Date="01-08-2003" Type="2"> <!-- G -->
   <ElemID>13</ElemID>
   <Name>G</Name>
 </Elem>
 <Elem Date="01-08-2003" Type="2"> <!-- H -->
   <ElemID>13</ElemID>
   <Name>H</Name>
 </Elem>
 <Elem Date="01-08-2003" Type="2"> <!-- I -->
   <ElemID>13</ElemID>
   <Name>I</Name>
 </Elem>
 <Elem Date="01-08-2003" Type="3"> <!-- J -->
   <ElemID>14</ElemID>
   <Name>J</Name>
 </Elem>
</Elements>
</root>

The caveat is, that I do not want elements (Elem) of Type="1" in the output.
With the help of Jeni T. at the time (limited by my understanding), I got the following XSL-fragment:


<xsl:template name="group-by-date">
<xsl:variable name="unique-dates" select="/root/Elements/Elem/[not(@Date=preceding-sibling::Elem/@Date)]/@Date"/>
<output>
<xsl:for-each select="$unique-dates">
<xsl:sort select="substring(.,$YearPos,4)" order="descending"/>
<xsl:sort select="substring(.,$MonthPos,2)" order="descending"/>
<xsl:sort select="substring(.,$DayPos,2)" order="descending"/>


<OutputByDate><Date><xsl:value-of select="."/></Date>

<xsl:for-each select="/root/Elements/Elem[@Date=current()][not(preceding-sibling::Elem[@Date=current()]/ElemID=ElemID][not(@Type='1' or

@Type='3']"/> <!-- This for-each line gives the problem -->
     <OutputElem><ID><xsl:value-of select="ElemID"/></ID>

<xsl:for-each select="/root/Elements/Elem[@Date=current()/@Date][ElemID=current()/ElemID][not(@Type='1' or

@Type='3']"/>

<OutputElemSub><Type><xsl:value-of select="@Type"/></OutputElemSub>

     </xsl:for-each> <!-- End of single Elem item -->
     </OutputElem>
   </xsl:for-each> <!-- End of cluster of Elem items -->
   </OutputByDate>
 </xsl:for-each> <!-- End of Date the items are grouped by -->
 </output>
</xsl:template>


<output> <OutputByDate><Date>01-08-2003</Date> <OutputElem><ID>13</OutputElem> <OutputElemSub><Type>2</Type><Name>G</Name></OutputElemSub> <OutputElemSub><Type>2</Type><Name>H</Name></OutputElemSub> <OutputElemSub><Type>2</Type><Name>I</Name></OutputElemSub> </OutputElem> <OutputElem><ID>14</ID> <OutputElemSub><Type>2</Type><Name>J</Name></OutputElemSub> </OutputElem> </OutputByDate>

 <OutputByDate><Date>01-04-2003</Date>
   <OutputElem><ID>10</OutputElem>
     <OutputElemSub><Type>2</Type><Name>D</Name></OutputElemSub>
   </OutputElem>
   <OutputElem><ID>13</OutputElem>
     <OutputElemSub><Type>2</Type><Name>E</Name></OutputElemSub>
   </OutputElem>
 </OutputByDate>
</output>


Elements <!-- F --> is rightfully not in the output as it is of @Type='1'


The problem is that so is the case of elements <!-- B --> and <!-- C -->. I wanted the same output as above but including

the following:

 <OutputByDate><Date>01-01-2003</Date>
   <OutputElem><ID>10</OutputElem>
     <OutputElemSub><Type>2</Type><Name>B</Name></OutputElemSub>
     <OutputElemSub><Type>2</Type><Name>C</Name></OutputElemSub>
   </OutputElem>
 </OutputByDate>

Notice that the <!-- A --> element - of @Type='1' - is not in the wanted output.


A further problem is that I cannot make the XPath exclusion of elements with their only children being of @Type='1' or


@Type='3' at the same time as grouping them.

I have tried making a variable which contains all the valid elements in the top of the template:
<xsl:variable name="ValidElems" select="/root/Elements/Elem[(@Type='2' or @Type='4')]"/>


The second for-each could then loop through this:
<xsl:for-each select="$ValidElems[@Date=current()][not(preceding-sibling::Elem[@Date=current()]/ElemID=ElemID]"/>
The problem is with the second predicate which goes through the input document, not only those contained in the variable


and which therefore will give <!-- F --> in the output too.

I do not know how to change this so that it does go through only the elements contained only in the variable $ValidItems

or how to make the solution otherwise.

I have nothing against key's if that is a solution, I have just never worked with key's in XSL, and is therefore not

proficient in this, but if a solutions with key's is easier, then I will very much want to learn.

Thanks in advance,
Ragulf Pickaxe :-|

_________________________________________________________________
Tired of spam? Get advanced junk mail protection with MSN 8. http://join.msn.com/?page=features/junkmail


Current Thread