Reputation: 3
I have the following XML :
<discrepancyModel>
<MissingSuppliers isMissingSuppliersCheckApplied="true">
<SnsCode value="str1234" mi="GRAA350RR" syst="29" subsys="2"
subsubsys="1" assy="XX" />
<SnsCode value="str1234" mi="GRAA350RR" syst="29" subsys="2"
subsubsys="3" assy="XX" />
<SnsCode value="str1234" mi="TRENTXWB" syst="28" subsys="1"
subsubsys="1" assy="XX" />
<SnsCode value="str1234" mi="TRENTXWB" syst="29" subsys="1"
subsubsys="1" assy="57" />
<SnsCode value="str1234" mi="TRENTXWC" syst="29" subsys="1"
subsubsys="1" assy="58" />
<SnsCode value="str1234" mi="TRENTXWD" syst="29" subsys="1"
subsubsys="1" assy="58" />
</MissingSuppliers>
</discrepancyModel>
The expected output is:
Here I have to display the information of each SNSCODE in a table row. If 2 or more SNSCODE have the same value of @mi, then SNSCODE is written only once but its has multiple rows in front of it (i.e 1st column is combined)
The main problem where I am stuck is that i want alternate colors for rows but i am not able to achieve that. I have seen all earlier answers but they do not solve my problem.
I have the following xslt code :
<table border="1">
<tr>
<th>Name (MI)</th>
<th>systemCode</th>
<th>subSystemCode</th>
<th>subSubSystemCode</th>
<th>assyCode</th>
</tr>
<xsl:for-each select="//SnsCode">
<xsl:variable name="tmp">
<xsl:value-of select="./@mi" />
</xsl:variable>
<xsl:if test="generate-id(.) = generate-id(key('stvalkey',$tmp)[1])">
<xsl:variable name="miNumber" select="count(key('stvalkey',$tmp))" />
<xsl:for-each select="//SnsCode[@mi=$tmp]">
<xsl:value-of select="count(preceding-sibling::SnsCode)" />
<tr>
<xsl:attribute name="class">
<xsl:if test="position() mod 2 = 0">
<xsl:value-of select="'odd'" />
</xsl:if>
</xsl:attribute>
<xsl:if test="position()=1">
<td>
<xsl:attribute name="rowspan">
<xsl:value-of select="$miNumber" />
</xsl:attribute>
<xsl:value-of select="$tmp" />
</td>
</xsl:if>
<td>
<xsl:value-of select="./@syst" />
</td>
<td>
<xsl:value-of select="./@subsys" />
</td>
<td>
<xsl:value-of select="./@subsubsys" />
</td>
<td>
<xsl:value-of select="./@assy" />
</td>
</tr>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</table>
This code used for coloring is:
<xsl:attribute name="class">
<xsl:if test="position() mod 2 = 0">
<xsl:value-of select="'odd'"/>
</xsl:if>
</xsl:attribute>
The problem with this is , if i use this code inside the loop, it does not color the whole row, it colors only 2nd row within a mi value.
On the other hand, if use this code outside the loop, then i do not get the position of elements as 1,2,3,4....but i get values as 1,3,5,6 because there are 2 occurences for 1st value and 2 occurence for 2nd value and so on.
Can anyone suggest how to do the coloring in this case ?
Upvotes: 0
Views: 296
Reputation: 4238
The following XSLT uses the overall structure given in the question. However, instead of using the position()
of the context to determine the odd table rows it defines a variable group_index
which determines how many groups precede the current group.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:key name="stvalkey" match="//SnsCode" use="@mi"/>
<xsl:template match="/">
<table border="1">
<tr>
<th>Name (MI)</th>
<th>systemCode</th>
<th>subSystemCode</th>
<th>subSubSystemCode</th>
<th>assyCode</th>
</tr>
<xsl:for-each select="//SnsCode">
<xsl:variable name="tmp" >
<xsl:value-of select="./@mi"/>
</xsl:variable>
<xsl:if test="generate-id(.) = generate-id(key('stvalkey',$tmp)[1])">
<xsl:variable name="group_index" select="count(preceding-sibling::SnsCode[generate-id(.) = generate-id(key('stvalkey',@mi)[1])])"/>
<xsl:variable name="miNumber" select="count(key('stvalkey',$tmp))" />
<xsl:for-each select="//SnsCode[@mi=$tmp]">
<xsl:value-of select="count(preceding-sibling::SnsCode)"/>
<tr>
<xsl:attribute name="style">
<xsl:if test="$group_index mod 2 = 0">
<xsl:value-of select="'background-color:yellow;'"/>
</xsl:if>
</xsl:attribute>
<xsl:if test="position()=1">
<td>
<xsl:attribute name="rowspan">
<xsl:value-of select="$miNumber" />
</xsl:attribute>
<xsl:value-of select="$tmp" />
</td>
</xsl:if>
<td><xsl:value-of select="./@syst" /></td>
<td><xsl:value-of select="./@subsys" /></td>
<td><xsl:value-of select="./@subsubsys" /></td>
<td><xsl:value-of select="./@assy" /></td>
</tr>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
resulting in this HTML output
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<table border="1">
<tr>
<th>Name (MI)</th>
<th>systemCode</th>
<th>subSystemCode</th>
<th>subSubSystemCode</th>
<th>assyCode</th>
</tr>0<tr style="background-color:yellow;">
<td rowspan="2">GRAA350RR</td>
<td>29</td>
<td>2</td>
<td>1</td>
<td>XX</td>
</tr>1<tr style="background-color:yellow;">
<td>29</td>
<td>2</td>
<td>3</td>
<td>XX</td>
</tr>2<tr style="">
<td rowspan="2">TRENTXWB</td>
<td>28</td>
<td>1</td>
<td>1</td>
<td>XX</td>
</tr>3<tr style="">
<td>29</td>
<td>1</td>
<td>1</td>
<td>57</td>
</tr>4<tr style="background-color:yellow;">
<td rowspan="1">TRENTXWC</td>
<td>29</td>
<td>1</td>
<td>1</td>
<td>58</td>
</tr>5<tr style="">
<td rowspan="1">TRENTXWD</td>
<td>29</td>
<td>1</td>
<td>1</td>
<td>58</td>
</tr>
</table>
Notes:
odd
was replaced by an inline definition to make the coloring visible in the resulting HTML page.Upvotes: 1
Reputation: 122374
If it's always the case that multiple records with the same @mi
are adjacent in the source XML then you don't need the two nested levels of for-each
, you could just do all the work in one for-each
and use the key to deal with the first column:
<table border="1">
<tr>
<th>Name (MI)</th>
<th>systemCode</th>
<th>subSystemCode</th>
<th>subSubSystemCode</th>
<th>assyCode</th>
</tr>
<xsl:for-each select="//SnsCode">
<xsl:variable name="tmp" select="@mi" />
<xsl:variable name="miNumber" select="count(key('stvalkey',$tmp))" />
<tr>
<xsl:attribute name="class">
<xsl:if test="position() mod 2 = 0">odd</xsl:if>
</xsl:attribute>
<xsl:if test="generate-id() = generate-id(key('stvalkey', $tmp)[1])">
<td>
<xsl:attribute name="rowspan">
<xsl:value-of select="$miNumber" />
</xsl:attribute>
<xsl:value-of select="$tmp" />
</td>
</xsl:if>
<td>
<xsl:value-of select="./@syst" />
</td>
<td>
<xsl:value-of select="./@subsys" />
</td>
<td>
<xsl:value-of select="./@subsubsys" />
</td>
<td>
<xsl:value-of select="./@assy" />
</td>
</tr>
</xsl:for-each>
</table>
Now you only have one for-each
the position()
will give you the right value each time.
Upvotes: 0