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[(_at_)Date=current()][not(preceding-sibling::Elem[(_at_)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[(_at_)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[(_at_)Date=current()][not(preceding-sibling::Elem[(_at_)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