I have an xml document. And i have some nodes. 1.Management(which has some Upravlinnya), 2.Upravlinnya(which has some viddils) and that viddils (it is like department)contains some workers And I solve my problem as you can see only with viddils. But i actually do not know why it is not working whith others. My Table output(by now):
Note that this is not a strictly correct solution. Ideally, the first row of every section should contain a cell for every column in the table, and likewise, the first row of each subsection should contain a cell for each of the columns remaining to the right-hand side of the subsection's column.
Because of this, the result is very slightly skewed: the top borders of the cells in the same row do not align perfectly. However, I believe it's a small price to pay for such simplicity of code.
CodePudding user response:
The key is to be able, for each row you want to build, to find out if the cells should be added with a rowspan that covers their descendant rows, which you were attempting to do with:
<xsl:if test="position() = 1">
But that test is only run in the context of Worker, so effectively your logic looks like this:
<xsl:if test="Worker first in Viddil">
<td rowspan="{last()}">
<xsl:value-of select="ancestor::Management[1]/ManagementName"/>
</td>
</xsl:if>
<xsl:if test="Worker first in Viddil">
<td rowspan="{last()}">
<xsl:value-of select="ancestor::Upravlinnya[1]/UpravlinnyaName"/>
</td>
</xsl:if>
<xsl:if test="Worker first in Viddil">
<td rowspan="{last()}">
<xsl:value-of select="ancestor::Viddil[1]/ViddilName"/>
</td>
</xsl:if>
and that's why only Viddil looks correct, because the logic is correct only for Viddil; and that same Viddil-only logic is applied to Management and Upravlinnya, and it looks wrong.
In my version, I'm still testing from the context of a Worker, but I'm "reaching back up the tree" to the nearest ancestor that matters, and asking if that ancestor is the first. I'm doing that with a combination of ancestor
and preceding-sibiling
axes:
<xsl:variable
name="firstUpravlinnya"
select="count(ancestor::Upravlinnya/preceding-sibling::Upravlinnya) = 0"/>
All that says is, "as a Worker, find my Upravlinnya, and is my Upravlinnya the first Upravlinnya amongst all the Upravlinnyas under my Management (no siblings before/preceding it)?"
I also chose not to do this all in the nested for-each
loops, which would work, but become difficult to read. This uses the natural template matching characteristics of XSLT to just traverse a tree of nodes:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Managements">
<html>
<body>
<table border="1">
<tr bgcolor="#9acd32">
<th>Management Name</th>
<th>Upravlinnya Name</th>
<th>Viddil Name</th>
<th>Name</th>
<th>Surname</th>
<th>Birth Year</th>
</tr>
<!-- Find all Workers, under /Managements (your "root") -->
<xsl:apply-templates select=".//Worker"/>
</table>
</body>
</html>
</xsl:template>
<!--
Simply match on Worker, and call row-builder to do actual work
-->
<xsl:template match="Worker">
<xsl:call-template name="row-builder"/>
</xsl:template>
<xsl:template name="row-builder">
<!--
From Worker, find relative positions of ancestors (if they're the first)
-->
<xsl:variable name="firstUpravlinnya" select="count(ancestor::Upravlinnya/preceding-sibling::Upravlinnya) = 0"/>
<xsl:variable name="firstViddil" select="count(ancestor::Viddil/preceding-sibling::Viddil) = 0"/>
<xsl:variable name="firstWorker" select="count(preceding-sibling::Worker) = 0"/>
<!--
Build a row from left to right, Management to Worker.
-->
<tr>
<!--
One Management cell for all descendant Upravlinnyas, Viddils, & Workers
-->
<xsl:if test="$firstUpravlinnya and $firstViddil and $firstWorker">
<xsl:variable name="mgmtRowSpan" select="count(ancestor::Management//Worker)"/>
<td rowspan="{$mgmtRowSpan}">
<xsl:value-of select="ancestor::Management/ManagementName"/>
</td>
</xsl:if>
<!--
One Upravlinnya cell for all descendant Viddils & Workers
-->
<xsl:if test="$firstViddil and $firstWorker">
<xsl:variable name="upravlinnyaRowSpan" select="count(ancestor::Upravlinnya//Worker)"/>
<td rowspan="{$upravlinnyaRowSpan}">
<xsl:value-of select="ancestor::Upravlinnya/UpravlinnyaName"/>
</td>
</xsl:if>
<!--
One Viddil cell for all descendant Workers
-->
<xsl:if test="$firstWorker">
<xsl:variable name="viddilRowSpan" select="count(ancestor::Viddil//Worker)"/>
<td rowspan="{$viddilRowSpan}">
<xsl:value-of select="ancestor::Viddil/ViddilName"/>
</td>
</xsl:if>
<!-- Always add cells for a Worker -->
<td><xsl:value-of select="WorkerName"/></td>
<td><xsl:value-of select="WorkerSurname"/></td>
<td><xsl:value-of select="BirthYear"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Here's how it looks: