Hi I am using XSLT 1.0. My input looks like,
<table>
<tr>
<td/>
<td/>
<td/>
</tr>
<tr>
<td/>
<td/>
<td/>
</tr>
<tr>
<td/>
<td/>
<td/&开发者_开发技巧gt;
<td/>
</tr>
</table>
I want to know the maximum number of td in node. In this case, the maximum number of td is in 3rd tr and so my output should be 4. Need a template to do this. Thanks in advance
Here is an example which does not use recursion. It just uses an xsl:for-each to loop through the TR elements, ordering by the number of TD elements in each. The first one is then the maximum.
The maximum is put in a variable, called maxCells which, as an example, is made an attribute of the table.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="table"/>
</xsl:template>
<xsl:template match="table">
<!-- Variable holding the maximum number of cells in a row -->
<xsl:variable name="maxCells">
<xsl:for-each select="tr">
<xsl:sort select="count(td)" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(td)"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- Copy the table, adding the maximum cells as an attribute -->
<xsl:copy>
<xsl:attribute name="MaxCells">
<xsl:value-of select="$maxCells"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- Identity Transform -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this stylesheet is applied to the provided table HTML above, the output is as follows:
<table MaxCells="4">
<tr>
<td/>
<td/>
<td/>
</tr>
<tr>
<td/>
<td/>
<td/>
</tr>
<tr>
<td/>
<td/>
<td/>
<td/>
</tr>
</table>
I'd like to know if you are really trying to count cells or rather columns. What would you consider the correct result for the following table?
<table>
<tr><td colspan="2"/><td/></tr>
<tr><td/><td colspan="2"/></tr>
</table>
If you're just counting cells then you expect 2 - and that's what both previous answers provide. The table has actually three columns, however, so if that's what you're looking for (like when converting XHTML tables to CALS) you need to tweak @Tim C's solution a bit: replace count(td) with sum(td/@colspan) + count(td[not(@colspan)]) both in the <xsl:sort/> element and in the <xsl:value-of/>.
Unfortunately, even then the calculation fails to provide correct column counts in all cases. For example, it comes up with 2 instead of 3 when given this:
<table>
<tr><td rowspan="2"/><td/></tr>
<tr><td/><td/></tr>
</table>
I have no idea how to solve that one. I've never seen it in live data either (knock on wood).
One more thing. My karma is not sufficient to comment on @Tim C's answer but I need to write this lest I forget: the stylesheet is wrong in that it sorts the cell counts lexically (i.e. it thinks "120"<"19"<"5") so if you have a row with 5 cells and another one with 10 you will get 5 as maximum. This is easy to fix by adding data-type="number" to the <xsl:sort/> tag.
You need to use recursion. Basically, the running-max template listed below runs for every tr child element of a table. It is first applied for the first child tr element, calculates the number of td elements, compares it to a running max, and then continues to do the same for following siblings, if there are any.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:variable name="count">
<xsl:apply-templates select="table"/>
</xsl:variable>
</xsl:template>
<xsl:template match="table">
<xsl:apply-templates select="tr[1]" mode="running-max"/>
</xsl:template>
<xsl:template match="tr" mode="running-max">
<xsl:param name="max" select="'0'"/>
<xsl:variable name="size" select="count(td)"/>
<xsl:variable name="newmax">
<xsl:choose>
<xsl:when test="$size > $max">
<xsl:value-of select="$size"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$max"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="following-sibling::tr">
<xsl:apply-templates select="following-sibling::tr[position()=1]" mode="running-max">
<xsl:with-param name="max" select="$newmax"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newmax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="table-temp">
<xsl:apply-templates select="*:table"/>
</xsl:variable>
<xsl:template match="/">
<root>
<xsl:for-each select="$table-temp/table/descendant::tr">
<cout>
<xsl:value-of select="count(child::td)"/>
</cout>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="*:table">
<xsl:element name="table">
<xsl:attribute name="style"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:tbody">
<xsl:element name="tbody">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:thead">
<xsl:element name="thead">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:tr">
<xsl:element name="tr">
<xsl:attribute name="style"/>
<xsl:variable name="rowspan">
<xsl:if test="preceding-sibling::*:tr/child::*:td/@rowspan">
<xsl:value-of select="sum(preceding-sibling::*:tr/child::*:td/@rowspan)"/>
</xsl:if>
<xsl:if test="preceding-sibling::*:tr/child::*:th/@rowspan">
<xsl:value-of select="sum(preceding-sibling::*:tr/child::*:th/@rowspan)"/>
</xsl:if>
</xsl:variable>
<xsl:variable name="td-pos" select="preceding-sibling::tr[child::td/@rowspan]/position()"/>
<xsl:variable name="th-pos" select="preceding-sibling::tr[child::th/@rowspan]/position()"/>
<xsl:for-each select="1 to $td-pos[last()]">
<xsl:element name="td"/>
</xsl:for-each>
<xsl:for-each select="1 to $th-pos[last()]">
<xsl:element name="td"/>
</xsl:for-each>
<xsl:if test="child::*:td/@colspan|child::*:th/@colspan">
<xsl:variable name="num">
<xsl:if test="child::*:th/@colspan">
<xsl:value-of select="sum(child::*:th/@colspan)-1"/>
</xsl:if>
<xsl:if test="child::*:td/@colspan">
<xsl:value-of select="sum(child::*:td/@colspan)-1"/>
</xsl:if>
</xsl:variable>
<xsl:for-each select="1 to $num">
<xsl:element name="td">
<xsl:attribute name="style"/>
</xsl:element>
</xsl:for-each>
</xsl:if>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:td">
<xsl:element name="td">
<xsl:attribute name="style"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:th">
<xsl:element name="td">
<xsl:attribute name="style"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
精彩评论