HTML/xml table to Latex Table - xslt

I am trying to transforming HTML table to LaTeX using XSLT, I have a problem for rowspan attribute.
if td[#rowspan >= 1] then, & should be placed on next row's corresponding td
ie., if td[#rowspan = 1], then & to be insert in the next rows corresponding tds
if td[#rowspan = 2], then & need to be insert in the next two rows corresponding tds
if td[#rowspan = 3], then & need to be insert in the next three rows corresponding tds
if td[#rowspan = 4], then & need to be insert in the next four rows corresponding tds
and so on...
MWE:
<?xml version="1.0" encoding="UTF-8"?>
<article>
<body>
<p>
<table>
<colgroup cols="4">
<col colnum="1" align="left" width="50pt"/>
<col colnum="2" align="left" width="50pt"/>
<col colnum="3" align="left" width="50pt"/>
<col colnum="4" align="left" width="50pt"/>
</colgroup>
<thead>
<tr><!--Col Head Row 1-->
<td valign="top" align="left" width="50pt">Column Head 1</td>
<td valign="top" align="left" width="50pt">Column Head 2</td>
<td valign="top" align="left" width="50pt">Column Head 3</td>
<td valign="top" align="left" width="50pt">Column Head 4</td>
</tr>
</thead>
<tbody>
<tr><!--Row 1-->
<td align="left">New</td>
<td align="left">some text</td>
<td align="left">Computer</td>
<td align="left">Microsoft, Apple Inc</td>
</tr>
<tr><!--Row 2-->
<td align="left">2nd row - 1st column</td>
<td align="left" colspan="2">2nd row - 2nd column</td>
<td align="left">2nd row - 3rd column</td>
</tr>
<tr><!--Row 3-->
<td rowspan="2" align="left">3rd row - 1st column</td>
<td align="left">3rd row - 2nd column</td>
<td align="left">3rd row - 3rd column</td>
</tr>
<tr><!--Row 4-->
<td align="left">4th row - 2nd column</td>
<td align="left">4th row - 3rd column</td>
</tr>
<tr><!--Row 5-->
<td align="left">5th row - 2nd column</td>
<td align="left">5th row - 3rd column</td>
</tr>
<tr><!--Row 6-->
<td align="left">6th row - 1st column</td>
<td rowspan="2" align="left">6th row - 2nd column</td>
<td align="left">6th row - 3rd column</td>
</tr>
<tr><!--Row 7-->
<td align="left">7th row - 1 column</td>
<td align="left">7th row - 3rd column</td>
</tr>
<tr><!--Row 8-->
<td align="left">8th row -1st column</td>
<td align="left">8th row -3rd column</td>
</tr>
</tbody>
</table>
</p>
</body>
</article>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
>
<xsl:output method="text" indent="no" />
<xsl:preserve-space elements="p"/>
<xsl:strip-space elements="*"/>
<xsl:template name="getMaxCols">
<xsl:for-each select="../../../tbody/tr">
<xsl:sort select="count(th|td)" data-type="number" order="descending" />
<xsl:if test="position()=1">
<xsl:variable name="maxCtr">
<xsl:choose>
<xsl:when test=".//*[#colspan]">
<xsl:sequence select="sum(.//#colspan)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="count(td)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$maxCtr" />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="getRowSpan">
<xsl:param name="totalcols" />
<xsl:param name="thisrow" />
<xsl:param name="thiscol" />
<xsl:param name="thisrowcols" />
<xsl:param name="thiscolspanpos" />
<xsl:param name="node" />
<xsl:choose>
<xsl:when test="not($totalcols = $thisrowcols)">
<!-- Loop through each rows from 1 to $thisrow -->
<xsl:for-each select="../../tr[position() > 0 and position() < $thisrow]">
<xsl:variable name="tmpRow" select="position()" />
<xsl:variable name="colspanCtr">0</xsl:variable>
<xsl:for-each select="child::*[position() < number($thiscol + 1)]">
<xsl:choose>
<xsl:when test="
number(count(preceding-sibling::*) + 1) = $thiscol and
#rowspan > 1 and
number(#rowspan + $tmpRow) > $thisrow
">
<xsl:for-each select="
../*[#rowspan > 1][following-sibling::*[#rowspan=1]]
">
<xsl:if test="position() > 0">&</xsl:if>
</xsl:for-each>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:when>
<xsl:otherwise/>
</xsl:choose><xsl:text/>
</xsl:template>
<xsl:template match="table">
<xsl:text/>\begin{tabular}{<xsl:for-each select="colgroup"><xsl:value-of select="substring(#align, 1, 1)" /></xsl:for-each>}
<xsl:text/>
<xsl:text/>
<xsl:apply-templates select="thead"/><xsl:text/>
<xsl:apply-templates select="tbody"/><xsl:text/>
<xsl:text/>\end{tabular}<xsl:text/>
</xsl:template>
<xsl:template match="thead"><xsl:apply-templates /></xsl:template>
<xsl:template match="tbody"><xsl:apply-templates /></xsl:template>
<xsl:template match="tr"><xsl:apply-templates />\\
%%
</xsl:template>
<xsl:template match="td">
<xsl:variable name="colspanpos">0</xsl:variable>
<xsl:variable name="rowpos"><xsl:value-of select="number(count(../preceding-sibling::tr) + 1)" /></xsl:variable>
<xsl:variable name="rowcols"><xsl:value-of select="number(count(../td))" /></xsl:variable>
<xsl:variable name="colpos"><xsl:value-of select="number(position() div 2)" /></xsl:variable>
<xsl:variable name="tabcols"><xsl:call-template name="getMaxCols"/></xsl:variable>
<xsl:variable name="colspanpos" select="sum(for $i in ../*[position() < $colpos + 1] return number($colspanpos + number($i/#colspan)))" />
<xsl:call-template name="getRowSpan">
<xsl:with-param name="totalcols" select="$tabcols"/>
<xsl:with-param name="thisrow" select="$rowpos" />
<xsl:with-param name="thisrowcols" select="$rowcols" />
<xsl:with-param name="thiscol" select="$colpos" />
<xsl:with-param name="thiscolspanpos" select="$colspanpos" />
<xsl:with-param name="node" select="local-name()" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="#colspan > 1">\multicolumn{<xsl:value-of select="#colspan" />}{<xsl:value-of select="substring(#align, 1, 1)" />}{<xsl:if test="#rowspan > 1">\multirow{<xsl:value-of select="#rowspan" />}{\linewidth}{</xsl:if><xsl:apply-templates /><xsl:if test="#rowspan > 1">}</xsl:if>}</xsl:when>
<xsl:when test=".=''"><xsl:apply-templates /></xsl:when>
<xsl:when test="#rowspan >= 1">\multirow{<xsl:value-of select="#rowspan" />}{\linewidth}{<xsl:apply-templates />}</xsl:when>
<xsl:otherwise><xsl:if test="#colspan='1' and #align='center'">\multicolumn{1}{c}</xsl:if>{<xsl:apply-templates />}</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::td"> & </xsl:if>
</xsl:template>
</xsl:stylesheet>
Expected Output:
\begin{tabular}{}
{Column Head 1} & {Column Head 2} & {Column Head 3} & {Column Head 4}\\
{New} & {some text} & {Computer} & {Microsoft, Apple Inc}\\
{2nd row - 1st column} & \multicolumn{2}{l}{2nd row - 2nd column} & {2nd row - 3rd column}\\
\multirow{2}{\linewidth}{3rd row - 1st column} & {3rd row - 2nd column} & {3rd row - 3rd column}\\
& {4th row - 2nd column} & {4th row - 3rd column}\\
& {5th row - 2nd column} & {5th row - 3rd column}\\
{6th row - 1st column} & \multirow{2}{\linewidth}{6th row - 2nd column} & {6th row - 3rd column}\\
{7th row - 1 column} & & {7th row - 3rd column}\\
{8th row -1st column} & & {8th row -3rd column}\\
\end{tabular}
but i am not getting proper output & not inserting in the produced output. how to convert LaTeX table.

Related

xslt 1.0 - display only one distinct value in a table cell as part of a multi-column table and alternate colors

I've got an xml file that has some data that is being output in a table split into two columns (effectively). This is the XML
<structuredBody>
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.3.1" />
<entry>
<organizer>
<component>
<observation>
<code displayName="TIBC" />
<effectiveTime value="8/29/2013 12:00:00 AM" />
<value value="39" />
<referenceRange>
<observationRange>
<text />
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="TSAT" />
<effectiveTime value="8/29/2013 12:00:00 AM" />
<value value="25" />
<referenceRange>
<observationRange>
<text />
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Albumin" />
<effectiveTime value="9/5/2013 12:00:00 AM" />
<value value="46" />
<referenceRange>
<observationRange>
<text />
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="ALT" />
<effectiveTime value="9/5/2013 12:00:00 AM" />
<value value="48" />
<referenceRange>
<observationRange>
<text>21-72</text>
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Bicarbonate" />
<effectiveTime value="9/5/2013 12:00:00 AM" />
<value value="69" />
<referenceRange>
<observationRange>
<text />
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
</section>
</component>
<component>
<section>
<...>
</section>
</component>
<component>
<section>
<...>
</section>
</component>
</structuredBody>
I've got the output formatted using xslt:
<xsl:template match="/">
<xsl:if test="//section[templateId/#root='2.16.840.1.113883.10.20.22.2.3.1']!=''">
<xsl:variable name="rowLabs" select="ceiling(count(//section[templateId/#root='2.16.840.1.113883.10.20.22.2.3.1']/entry) div $colLabs)" />
<div style="margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #000000;">
<span style="font-weight: bold;">Lab Results:</span>
<table border="0" cellspacing="0" cellpadding="1" width="99%" style="font-size: 11px;">
<xsl:for-each select="//section[templateId/#root='2.16.840.1.113883.10.20.22.2.3.1']/entry[position() <= $rowLabs]">
<tr>
<xsl:variable name="otherEntries" select=".|following-sibling::entry[position() mod $rowLabs = 0]" />
<xsl:apply-templates select="self::*|$otherEntries" />
<xsl:call-template name="blankentries">
<xsl:with-param name="entries" select="$colLabs - count($otherEntries) - 1" />
</xsl:call-template>
<!--<xsl:apply-templates select=".|following-sibling::entry[position() < 2]" />-->
</tr>
</xsl:for-each>
</table>
</div>
</xsl:if>
</xsl:template>
<xsl:template match="section[templateId/#root='2.16.840.1.113883.10.20.22.2.3.1']/entry">
<td width="75">
<xsl:call-template name="StripTime">
<xsl:with-param name="DateTime" select="organizer/component/observation/effectiveTime/#value" />
</xsl:call-template>
</td>
<td width="198">
<xsl:value-of select="organizer/component/observation/code/#displayName"/>
</td>
<td width="50">
<xsl:value-of select="organizer/component/observation/value/#value"/>
</td>
<td width="75">
<xsl:value-of select="organizer/component/observation/referenceRange/observationRange/text"/>
</td>
</xsl:template>
<xsl:template name="blankentries">
<xsl:param name="entries" />
<xsl:if test="$entries > 0">
<td></td>
<xsl:call-template name="blankentries">
<xsl:with-param name="entries" select="$entries - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
so that the resulting entry nodes run down and then over so that the output is:
<table>
<tr>
<td>8/29/2013</td>
<td>TIBC</td>
<td>39</td>
<td></td>
<td>9/5/2013</td>
<td>ALT</td>
<td>48</td>
<td>21-72</td>
</tr>
<tr>
<td>8/29/2013</td>
<td>TSAT</td>
<td>25</td>
<td></td>
<td>9/5/2013</td>
<td>Bicarbonate</td>
<td>69</td>
<td></td>
</tr>
<tr>
<td>9/5/2013</td>
<td>Albumin</td>
<td>46</td>
<td></td>
</tr>
</table>
This gives me:
[entry 1] [entry 4]
[entry 2] [entry 5]
[entry 3]
which is what I'm looking for and that's great.
What I can't figure out is how to get the different 4-cell entry sets to alternate colors as it goes through. I can't use position() because I'm manipulating it to get the desired output order in the table. If I output the position to figure out the math, on the left column, it's always "1" and on the right column it's always "2" so I can't do a position() mod 2 = 1 to set a style attribute.
Secondly, I only want the date value to appear one time and then not appear until it changes. That would make it such that the output ideally should look like:
<table>
<tr>
<td>8/29/2013</td>
<td>TIBC</td>
<td>39</td>
<td></td>
<td bgcolor="dcdcdc"></td>
<td bgcolor="dcdcdc">ALT</td>
<td bgcolor="dcdcdc">48</td>
<td bgcolor="dcdcdc">21-72</td>
</tr>
<tr>
<td bgcolor="dcdcdc"></td>
<td bgcolor="dcdcdc">TSAT</td>
<td bgcolor="dcdcdc">25</td>
<td bgcolor="dcdcdc"></td>
<td></td>
<td>Bicarbonate</td>
<td>69</td>
<td></td>
</tr>
<tr>
<td>9/5/2013</td>
<td>Albumin</td>
<td>46</td>
<td></td>
</tr>
</table>
I can't put the bgcolor attribute in the "tr" tag because it should alternate even across the "columns" and not just the entire row.
Thanks for any help. This site has brought my xslt knowledge a long way. I've only started delving into it in the last month.
For the colours you essentially have two cases depending on whether $rowLabs is odd or even.
If it's even then odd rows will be entirely "white" and even rows will be entirely "black"
If it's odd then
for odd rows, odd columns will be white and even ones black
for even rows, odd columns will be black and even ones white
("odd" and "even" counting from 1 as XPath position() does, so the first row/column is odd, the second is even, etc.).
You can encode that logic in XSLT by adding some parameters to the templates. Replace the
<xsl:apply-templates select="self::*|$otherEntries" />
with
<xsl:apply-templates select="self::*|$otherEntries">
<xsl:with-param name="rowNum" select="position()" />
<xsl:with-param name="totalRows" select="$rowLabs" />
</xsl:apply-templates>
and add the parameters to the section template
<xsl:template match="section[templateId/#root='2.16.840.1.113883.10.20.22.2.3.1']/entry">
<xsl:param name="rowNum" select="1" />
<xsl:param name="totalRows" select="2" />
Now we need a named template we can call to implement the logic I described above:
<xsl:template name="bgcolor">
<xsl:param name="rowNum" select="1" />
<xsl:param name="totalRows" select="2" />
<xsl:if test="($totalRows mod 2 = 0 and $rowNum mod 2 = 0) or
($totalRows mod 2 = 1 and $rowNum mod 2 != position() mod 2)">
<xsl:attribute name="bgcolor">dcdcdc</xsl:attribute>
</xsl:if>
</xsl:template>
This adds the bgcolor attribute if either there is an even number of rows and the current rownum is even, or if there is an odd number of rows and the current column number within the row has a "different oddness" from the row number.
Finally we call this template within the <td> elements, e.g.
<td width="50">
<xsl:call-template name="bgcolor">
<xsl:with-param name="rowNum" select="$rowNum" />
<xsl:with-param name="totalRows" select="$totalRows" />
</xsl:call-template>
<xsl:value-of select="organizer/component/observation/value/#value"/>
</td>
The crucial thing that makes all this work is the fact that you're applying templates to self::*|$otherEntries, so a call to position() within the applied template gives the column number (the position within this node list), not the node's original position in its parent.
To make the date appear only once the first time it is encountered, you can define a key and use a trick related to the "Muenchian grouping" technique. Declare
<xsl:key name="effectiveTimeByDate" match="effectiveTime"
use="substring-before(#value, ' ')" />
and then you can check whether this is the first occurrence of a particular date in the document using
<td width="75">
<xsl:call-template name="bgcolor">
<xsl:with-param name="rowNum" select="$rowNum" />
<xsl:with-param name="totalRows" select="$totalRows" />
</xsl:call-template>
<xsl:if test="
generate-id(organizer/component/observation/effectiveTime)
= generate-id(key('effectiveTimeByDate', substring-before(
organizer/component/observation/effectiveTime/#value, ' '))[1])">
<xsl:call-template name="StripTime">
<xsl:with-param name="DateTime" select="organizer/component/observation/effectiveTime/#value" />
</xsl:call-template>
</xsl:if>
</td>
(you may be able to scrap the StripTime template entirely and just use the substring-before call the same as I'm using in the key)

XSLT foreach loop

I have only been introduced to xslt and xml in the last few weeks, and I urgently need some help to write some code to achieve what I want to achieve.
I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<abc1 formName="Form">
<Level1>
<Element1>ZZZ</Element1>
<Element2>
<SubElement1>Apples</SubElement1>
<SubElement2>Oranges</SubElement2>
<SubElement3>Pears</SubElement3>
<SubElement4>Blueberries</SubElement4>
<SubElement5>Milkshakes</SubElement5>
</Element2>
</Level1>
<Level1>
<Element1>XXX</Element1>
<Element2>
<SubElement1>Apples</SubElement1>
<SubElement2>Oranges</SubElement2>
<SubElement3>Kiwifruit</SubElement3>
<SubElement4>Blueberries</SubElement4>
<SubElement5>Soda</SubElement5>
</Element2>
</Level1>
</abc1>
and the following html table:
<table>
<tr>
<td width="180" > Row 1</td>
<td width="540" colspan="4"> Cell_A</td>
</tr>
<tr>
<td width="180" >Types</td>
<td width="180" >Type 1</td>
<td width="180" >Type 2</td>
<td width="180" >Type 3</td>
</tr>
<tr>
<td width="180" >Row 2</td>
<td width="180" >Cell_B</td>
<td width="180" >Cell_C</td>
<td width="180" >Cell_D</td>
</tr>
<tr>
<td width="180" > Row 3</td>
<td width="180" >Cell_E</td>
<td width="180" >Cell_F</td>
<td width="180" >Cell_G</td>
</tr>
<tr>
<td width="180" >Row 4</td>
<td width="180" >Cell_H</td>
<td width="180" >Cell_I</td>
<td width="180" >Cell_J</td>
</tr>
<tr>
<td width="180" > Row 5</td>
<td width="540" colspan="4"> Cell_K</td>
</tr>
</table>
I am having trouble using xslt to extract the data from the xml into the table because the rules that I need to apply make it very complicated. Below are the rules that need to apply to the cells that I am having troubles with.
(1) If the <Element1> value is the same throughout the xml, then:
If <Element1> = 'ZZZ' then Cell_B = '10', Cell_C = '20', and Cell_D = '30'
If <Element1> = 'XXX' then Cell_B = '100', Cell_C = '90', and Cell_D = '80'
but if the <Element1> value differs in the xml, then:
Cell_B = '10,100', Cell_C = '20,90', and Cell_D = '30,80'
(2) If the <SubElement5> value is the same throughout the xml, then:
Cell_J = the value of <SubElement5>
but if the <SubElement5> value differs in the xml, then:
Cell_J = the value of both <SubElement5> values separated by a comma
so in this case, the value for Cell_J would be 'Milkshakes, Soda'.
I have been experimenting with different things using:
<xsl:for-each select="./Level1/Element2">
<xsl:value-of select="./SubElement5"/>
</xsl:for-each>
but I cant determine what code I can use to check if they are the same because I can't overwrite the value of a variable.
Edit:
Please note that the cells I've indicated above (cells b,c,d, and j) are the only ones I
need help with. Additionally, for Element1 there are four potential values I can
encounter: ZZZ, XXX, AAA, and BBB. The required values for each of these would be:
(cells b,c, and d)
ZZZ:10,20,30
XXX:100,90,80
AAA:40,30,30
BBB:50,30,20
so if there was only one potential value in the whole xml, the cells should appear with the values as above. If the two element 1 values are different, then the cells should list the above values in each cell, separated by a comma.
With respect to cell_j, I'll try and explain it a little better.
First, I need to determine if <SubElement5> is the same value throughout the xml. In this case it is not, in one section it is Milkshakes and in the other it is 'Soda'. Therefore, cell_J should contain the text 'Milkshakes, Soda'.
If the xml looked like this:
<?xml version="1.0" encoding="UTF-8"?>
<abc1 formName="Form">
<Level1>
<Element1>ZZZ</Element1>
<Element2>
<SubElement1>Apples</SubElement1>
<SubElement2>Oranges</SubElement2>
<SubElement3>Pears</SubElement3>
<SubElement4>Blueberries</SubElement4>
<SubElement5>Milkshakes</SubElement5>
</Element2>
</Level1>
<Level1>
<Element1>XXX</Element1>
<Element2>
<SubElement1>Apples</SubElement1>
<SubElement2>Oranges</SubElement2>
<SubElement3>Kiwifruit</SubElement3>
<SubElement4>Blueberries</SubElement4>
<SubElement5>Milkshakes</SubElement5>
</Element2>
</Level1>
</abc1>
Then the value for cell_j would just be 'Milkshakes'.
Thanks in advance to anyone who can help.
ANSWER TO THIS QUESTION:
To Summarize what Woody did below for anybody's future reference:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/abc1">
<xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/>
<table>
<tr>
<td width="180" > Row 1</td>
<td width="540" colspan="4"> Cell_A</td>
</tr>
<tr>
<td width="180" >Types</td>
<td width="180" >Type 1</td>
<td width="180" >Type 2</td>
<td width="180" >Type 3</td>
</tr>
<tr>
<td width="180" >Row 2</td>
<td width="180" > <xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">40</xsl:when>
<xsl:when test=". = 'BBB'">50</xsl:when>
<xsl:when test=". = 'XXX'">100</xsl:when>
<xsl:when test=". = 'ZZZ'">10</xsl:when>
</xsl:choose>
</xsl:for-each></td>
<td width="180" ><xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">30</xsl:when>
<xsl:when test=". = 'BBB'">30</xsl:when>
<xsl:when test=". = 'XXX'">90</xsl:when>
<xsl:when test=". = 'ZZZ'">20</xsl:when>
</xsl:choose>
</xsl:for-each></td>
<td width="180" ><xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">30</xsl:when>
<xsl:when test=". = 'BBB'">20</xsl:when>
<xsl:when test=". = 'XXX'">80</xsl:when>
<xsl:when test=". = 'ZZZ'">30</xsl:when>
</xsl:choose>
</xsl:for-each></td>
</tr>
<tr>
<td width="180" > Row 3</td>
<td width="180" >Cell_E</td>
<td width="180" >Cell_F</td>
<td width="180" >Cell_G</td>
</tr>
<tr>
<td width="180" >Row 4</td>
<td width="180" >Cell_H</td>
<td width="180" >Cell_I</td>
<td width="180" ><xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="."/>
</xsl:for-each></td>
</tr>
<tr>
<td width="180" > Row 5</td>
<td width="540" colspan="4"> Cell_K</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
Woody, thank-you again. This was awesome.
THe key with your problem is that you can't itterate through the data to get what you want, as the answers you want are fixed, ie, if this, output that, if that, output this other. So you need to take each section at a time.
Are there only ever two Level1s?
<xsl:template match="/abc1">
<table>
<tr>
<td width="180" >Row 2</td>
<xsl:choose>
<xsl:when test="not(Level1/Element1='XXX')">
<!-- only ZZZ -->
<td width="180" >10</td>
<td width="180" >20</td>
<td width="180" >30</td>
</xsl:when>
<xsl:when test="not(Level1/Element1='ZZZ')">
<!-- only YYY -->
<td width="180" >100</td>
<td width="180" >90</td>
<td width="180" >80</td>
</xsl:when>
<xsl:otherwise>
<!-- some combination -->
<td width="180" >10,100</td>
<td width="180" >20,90</td>
<td width="180" >30,80</td>
</xsl:otherwise>
</xsl:choose>
</tr>
.. continued
</table>
</xsl:template>
</xsl:stylesheet>
and so on per section. If you have a number of rows, and you want them comma separated then you need to do it per section
or if you have to put one comma list for each section.
Sorry, I couldn't see what you were trying to do with the cell_j and there didn't seem to be a rule for the other ones
Edit: However, if you have a lot of items rather than just 2, and you need a comma separated list, you can do it with xpath, so:
<tr>
<td>
<xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</td>
.. maybe other rows the same
</tr>
Edit again:
So for your updated question about cell_j, the above comes up with the correct values for it.
For your update in the first section, it would be possible to do a variation on that same theme, if you want all the values shown (so there are 4 values if you have all of your 4 options). Unfortunately as you have fixed values for each, and you have to do each one by one, you would need to do it in a large section, so in a loop:
<xsl:for-each select="//Element1[not(preceding::Element1/text() = text())]">
which would put you in a loop for each unique entry and then have a choose element based on whether that value was XXX, ZZZ etc.
Edit again
There are several ways of doing the first section you want, including recursive functions, external documents and using the node-set function of various different XSLT implimentations, but as a totally generic easy to see way, this is the slightly wordy, but easy to see version (I hope):
<xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/>
<table>
<tr>
<td width="180" >Row 2</td>
<td>
<xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">40</xsl:when>
<xsl:when test=". = 'BBB'">50</xsl:when>
<xsl:when test=". = 'XXX'">100</xsl:when>
<xsl:when test=". = 'ZZZ'">10</xsl:when>
</xsl:choose>
</xsl:for-each>
</td>
<td>
<xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">30</xsl:when>
<xsl:when test=". = 'BBB'">30</xsl:when>
<xsl:when test=". = 'XXX'">90</xsl:when>
<xsl:when test=". = 'ZZZ'">20</xsl:when>
</xsl:choose>
</xsl:for-each>
</td>
<td>
<xsl:for-each select="$elements">
<xsl:if test="position() != 1">,</xsl:if>
<xsl:choose>
<xsl:when test=". = 'AAA'">30</xsl:when>
<xsl:when test=". = 'BBB'">20</xsl:when>
<xsl:when test=". = 'XXX'">80</xsl:when>
<xsl:when test=". = 'ZZZ'">30</xsl:when>
</xsl:choose>
</xsl:for-each>
</td>
</tr>
</table>
actually - I am not sure if the iterating on a variable ($elements in this case) is that standard, it may be a saxon and msxsl thing, but if it isn't, you can replace it with the value (//Element1[not(preceding::Element1 = .)])

Display an XML attribute value only on the first encounter with XSL

I have this XML fragment:
<svrl:active-pattern document="file:/D:/3.4Workspace/antSchematronTest/xml/CDA.xml"
id="p-2.16.840.1.113883.10.20.1.31-warning"
name="Result observation - warning validation phase"/>
<svrl:fired-rule context="*[cda:templateId/#root="2.16.840.1.113883.10.20.1.31"]"/>
<svrl:failed-assert test="cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="count(cda:effectiveTime)=1"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="cda:referenceRange"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.</svrl:text>
</svrl:failed-assert>
<svrl:fired-rule context="*[cda:templateId/#root="2.16.840.1.113883.10.20.1.31"]"/>
<svrl:failed-assert test="cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][2]/*[local-name()='observation']">
<svrl:text>The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).</svrl:text>
</svrl:failed-assert>
I'm applying this XSL template to the fragment:
<xsl:template name="svrl:failed-assert" match="svrl:failed-assert">
<p><u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]" />
</u></p>
<table width="800">
<tr>
<td colspan="2"><font color="red"><xsl:value-of select="svrl:text" />
</font></td>
</tr>
<tr>
<td width="50">Location:</td>
<td width="750">
<i>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="#location"/>
<xsl:with-param name="replace" select=""*[local-name()='""/>
<xsl:with-param name="by" select="''"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="replace" select=""']""/>
<xsl:with-param name="by" select="''"/>
</xsl:call-template>
</i></td>
</tr>
<tr>
<td width="50">Test:;</td>
<td width="750"><i><xsl:value-of select="#test" /></i></td>
</tr>
</table>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This is the output:
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
Result observation - warning validation phase
A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; count(cda:effectiveTime)=1
Result observation - warning validation phase
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:referenceRange
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
I want the svrl:active-pattern#name-“Result observation - warning validation phase” to display only once. For example:
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; count(cda:effectiveTime)=1
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:referenceRange
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:referenceRange
How can I achieve this?
It looks like you only want to output the paragraph for the "Result observation - warning validation phase" message for the first svrl:failed-assert element you find in your XML, but not for any of the others.
What you need to do is have a template to match the first such element, which can output the first paragraph, and then calls the named template svrl:failed-assert which will also match all the other elements too.
<xsl:template match="svrl:failed-assert[1]">
<p>
<u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]"/>
</u>
</p>
<xsl:call-template name="svrl:failed-assert"/>
</xsl:template>
<xsl:template match="svrl:failed-assert" name="svrl:failed-assert">
<!-- Existing code here -->
Here is a simplified XSLT (without any find-and-replace code)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:svrl="oh:no" exclude-result-prefixes="svrl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="svrl:failed-assert[1]">
<p>
<u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]"/>
</u>
</p>
<xsl:call-template name="svrl:failed-assert"/>
</xsl:template>
<xsl:template match="svrl:failed-assert" name="svrl:failed-assert">
<table width="800">
<tr>
<td colspan="2">
<font color="red">
<xsl:value-of select="svrl:text"/>
</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>
<xsl:value-of select="#test"/>
</i>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
When applied to the following simplified XML:
<a xmlns:svrl="oh:no">
<svrl:active-pattern name="Result observation - warning validation phase"/>
<svrl:fired-rule context="Context 1"/>
<svrl:failed-assert test="Test 1" location="Location 1">
<svrl:text>Text 1</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="Test 2" location="Location 2">
<svrl:text>Text 2</svrl:text>
</svrl:failed-assert>
<svrl:fired-rule context="Context 2"/>
<svrl:failed-assert test="Test 3" location="Location 3">
<svrl:text>Text 3</svrl:text>
</svrl:failed-assert>
</a>
The following HTML is output:
<p>
<u>Result observation - warning validation phase</u>
</p>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 1</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 1</i>
</td>
</tr>
</table>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 2</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 2</i>
</td>
</tr>
</table>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 3</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 3</i>
</td>
</tr>
</table>

Getting an attribute via another attribute in XSL

I stink at XSLT, so I'm not sure how to approach this... I'm getting a feed from the EQ2 database. The XML looks like this:
<guilds limit="1" returned="1">
<guild accounts="3" alignment="0" dateformed="1127855265" guildid="111" guildstatus="0" id="1111111111" last_update="1326986410" level="11" name="MyGuild" version="1" world="Permafrost" worldid="202">
<ranks>
<rank id="0" name="Leader"/>
<rank id="1" name="Senior Officer"/>
<rank id="2" name="Officer"/>
<rank id="3" name="Senior Member"/>
<rank id="4" name="Member"/>
<rank id="5" name="Junior Member"/>
<rank id="6" name="Initiate"/>
<rank id="7" name="Recruit"/>
</ranks>
<members>
<member dbid="123456" rank="0" name="Dude1"/>
<member dbid="123457" rank="1" name="Dude2"/>
<member dbid="123458" rank="2" name="Dude3"/>
<member dbid="123459" rank="4" name="Dude4"/>
<member dbid="123460" rank="4" name="Dude5"/>
<member dbid="123461" rank="4" name="Dude6"/>
</members>
<events/>
</guild>
</guilds>
I'm trying to add the ranks into a table. The XSL (version 1) snippet for it is as follows, but adding the name from the rank isn't working properly - I know this part is wrong:
<xsl:value-of select="rank/rank[#id=1]/#name"/>
So, can I get some help making it work and maybe an idea of how to shorten it?
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="4.0" indent="yes"/>
<xsl:template match="/">
<table width="100%" cellspacing="0" cellpadding="0" id="eq2roster" align="center">
<thead>
<tr>
<td colspan="4" class="eq2CharacterHeader">Character</td>
<td colspan="3" class="eq2TradeskillsHeader">Tradeskills</td>
</tr>
<tr class="ForumCategoryHeader">
<th class="eq2NameHeader">Name</th>
<th class="eq2RankHeader">Rank</th>
<th class="eq2ClassHeader">Class</th>
<th class="eq2LevelHeader">Level</th>
<th class="eq2ArtisanHeader">Artisan</th>
<th class="eq2ArtisanLevelHeader">Level</th>
<th class="eq2SecondaryHeader">Secondary</th>
</tr>
</thead>
<tbody>
<xsl:for-each select="guilds/guild/members/member">
<xsl:if test="#id > 1">
<tr>
<td class="eq2Name">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:text>http://eq2players.station.sony.com/</xsl:text>
<xsl:value-of select="concat(normalize-space(/guilds/guild/#world), '/')"/>
<xsl:value-of select="concat(normalize-space(#name), '/')"/>
</xsl:attribute>
<xsl:attribute name="target">
<xsl:text>_blank</xsl:text>
</xsl:attribute>
<xsl:value-of select="normalize-space(#name)"/>
</xsl:element>
</td>
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>eq2Rank eq2rank-</xsl:text>
<xsl:value-of select="translate(normalize-space(guild/#rank),' ','')"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="member/#rank = 1"><span class = "rank1"><xsl:value-of select="ranks/rank[#id=1]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 2"><span class = "rank2"><xsl:value-of select="ranks/rank[#id=2]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 3"><span class = "rank3"><xsl:value-of select="ranks/rank[#id=3]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 4"><span class = "rank4"><xsl:value-of select="ranks/rank[#id=4]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 5"><span class = "rank5"><xsl:value-of select="ranks/rank[#id=5]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 6"><span class = "rank6"><xsl:value-of select="ranks/rank[#id=6]/#name"/></span></xsl:when>
<xsl:when test="member/#rank = 7"><span class = "rank7"><xsl:value-of select="ranks/rank[#id=7]/#name"/></span></xsl:when>
</xsl:choose>
</xsl:element>
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>eq2Class eq2</xsl:text>
<xsl:value-of select="translate(normalize-space(type/#class),' ','')"/>
</xsl:attribute>
<xsl:value-of select="type/#class"/>
</xsl:element>
<td class="eq2level"><xsl:value-of select="type/#level"/></td>
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>eq2ArtisanClass eq2</xsl:text>
<xsl:value-of select="translate(normalize-space(tradeskills/tradeskill/#class),' ','')"/>
</xsl:attribute>
<xsl:value-of select="tradeskills/tradeskill/#class"/>
</xsl:element>
<td class="eq2ArtisanLevel"><xsl:value-of select="tradeskills/tradeskill/#level"/></td>
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>eq2Secondary eq2</xsl:text>
<xsl:value-of select="translate(normalize-space(secondarytradeskills/secondarytradeskill/#name),' ','')"/>
</xsl:attribute>
<xsl:value-of select="secondarytradeskills/secondarytradeskill/#name"/>
</xsl:element>
</tr>
</xsl:if>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
The following expression associates a member with its corresponding rank using current() (assuming that the context node is the member in question):
../../ranks/rank[#id=current()/#rank]/#name
Full example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="member">
<td class="eq2Rank eq2rank-{guild/#rank}">
<span class="rank{#rank}">
<xsl:value-of
select="../../ranks/rank[#id=current()/#rank]/#name"/>
</span>
</td>
</xsl:template>
</xsl:stylesheet>
Output (based on the originally posted sample XML):
<td class="eq2Rank eq2rank-">
<span class="rank0">Leader</span>
</td>
<td class="eq2Rank eq2rank-">
<span class="rank1">Senior Officer</span>
</td>
<td class="eq2Rank eq2rank-">
<span class="rank2">Officer</span>
</td>
<td class="eq2Rank eq2rank-">
<span class="rank4">Member</span>
</td>
<td class="eq2Rank eq2rank-">
<span class="rank4">Member</span>
</td>
<td class="eq2Rank eq2rank-">
<span class="rank4">Member</span>
</td>
Not sure if you can do something like this but it might be worth a shot. In the context of a single member
<xsl:value = "//ranks/rank[#id = ./#rank]/#name"/>
It uses X-Path to jump to the top and look for the ranks node, find the rank element whose ID attribute equals the rank attribute of the member, and grabs the name attribute
It's been a few years since I had to XSL like that, but should allow you to get rid of the XSL:choose block
I'm not really sure if I get the question.
Asume that you have the right rankid you probably want to use variable
<xsl:variable name="rank" select="translate(normalize-space(guild/#rank),' ','')"/>
<span class = "rank{$rank}"><xsl:value-of select="ranks/rank[#id = $rank]/#name"/></span>

Alternate Row Color of Table using XSLT

I have an XSLT where I want to alternate the row colors of an output table. I know that you can use the code such as in this example:
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="current()/#name"/></td>
<td><xsl:value-of select="current()/#ID"/></td>
</tr>
</xsl:for-each>
</table>
which works fine however, I have a few instances where I need to include some if statements within the for-each, such as.
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="current()/#ID > 10000 and current/#ID < 6000">
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="current()/#name"/></td>
<td><xsl:value-of select="current()/#ID"/></td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
Then it doesn't work because it may skip an item at a position in the for-each and I end up with randomly alternating row colors or it may start at an incorrect position where the rows alternate starting with the incorrect color.
I've tried adding an xsl:sort, which doesn't really fix the problem. Is there a way to avoid this snag?
Try with the following code:
tr[position() mod 2 =0]
<xsl:template match="n1:tr[position() mod 2 =0]">
<tr bgcolor="#aaaaaa">
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="n1:tr[position() mod 2 =1]">
<tr bgcolor="#aaaaff">
<xsl:apply-templates/>
</tr>
</xsl:template>
The simplest solution (taking your sample stylesheet as representative of your actual needs) is to only loop over the desired nodes in the first place. Like this:
<xsl:template match="/">
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book[#ID < 10000 and #ID > 6000]">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="#name" /></td>
<td><xsl:value-of select="#ID" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
In other words, instead of conditionally including nodes in the body of the for-each simply select only those nodes you need. By doing this, position() will be relative to the set that you're iterating and it will work as expected.
For example, this (simplified) input:
<r>
<Book ID="6200"/>
<Book ID="7100"/>
<Book/>
<Book/>
<Book ID="8000"/>
<Book/>
<Book ID="9001"/>
<Book ID="9002"/>
</r>
Produces the correct alternation:
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>6200</td>
</tr>
<tr bgcolor="#FFFFFF">
<td />
<td>7100</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>8000</td>
</tr>
<tr bgcolor="#FFFFFF">
<td />
<td>9001</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>9002</td>
</tr>
</table>
one simple solution would be to remember last color used and than use opposing one
instead basing your choice on position() and also you should move choice inside test for row
here is pseudo code
currentColor = color1
for each
if ( id > 10000 and id < 6000 ) {
if ( currentColor == color1 )
currentColor= color2
else
currentColor = color1
showDataInColor(currentColor)
}