如何计算和评估 XML 中的节点以在 HTML table 中创建行跨度值?

How to count and evaluate nodes in XML to create rowspan values in HTML table?

我有一份 xml 文件。我有一些节点。 1.Management(有一些 Upravlinnya),2.Upravlinnya(有一些 viddils)并且 viddils(就像部门)包含一些工人 我解决了我的问题,正如您只能通过 viddils 看到的那样。但我实际上不知道为什么它对其他人不起作用。 我的 Table 输出(现在): table

以及它应该如何 how should be 我的 Xslt 文档

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
    <h2>Labka -7 Kondera Sviatoslav</h2>
    <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>
      <xsl:for-each select="Managements/Management">
        <xsl:for-each select="Upravlinnyas/Upravlinnya">
          <xsl:for-each select="Viddils/Viddil">
            <xsl:for-each select="Workers/Worker">
              <tr>
                 <xsl:if test="position() = 1">
                 <td rowspan="{last()}"> 
                  <xsl:value-of select="ancestor::Management[1]/ManagementName"/>
                 </td>
                 </xsl:if>
                 <xsl:if test="position() = 1">
                 <td rowspan="{last()}"> 
                  <xsl:value-of select="ancestor::Upravlinnya[1]/UpravlinnyaName"/>
                 </td>
                 </xsl:if>
                   <xsl:if test="position() = 1">
                     <td rowspan="{last()}"> 
                     <xsl:value-of select="ancestor::Viddil[1]/ViddilName"/> 
                   </td>
                 </xsl:if>
                  <td><xsl:value-of select="WorkerName"/></td>
                  <td><xsl:value-of select="WorkerSurname"/></td>
                  <td><xsl:value-of select="BirthYear"/></td>
             </tr>    
            </xsl:for-each>  
                      
          </xsl:for-each>           
      </xsl:for-each>      
     </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

还有我的 xml 文件(我文件的一部分)

<?xml-stylesheet type = "text/xsl" href = "labka.xsl"?>
<Managements>
  <Management id="1111">
    <ManagementName>Management_1</ManagementName>
    <Upravlinnyas>
      <Upravlinnya id="1111">
        <UpravlinnyaName>Upravlinnya_1</UpravlinnyaName>
        <Viddils>
          <Viddil id ="1111">
            <ViddilName>Viddil_1</ViddilName>
            <Workers>
              <Worker id="1111">
                <WorkerName>Sviatoslav</WorkerName>
                <WorkerSurname>Kondera</WorkerSurname>
                <BirthYear>2002</BirthYear>
              </Worker>
              <Worker id="2222">
                <WorkerName>Orest</WorkerName>
                <WorkerSurname>Rodcevich</WorkerSurname>
                <BirthYear>1999</BirthYear>
              </Worker>
              <Worker id="3333">
                <WorkerName>Olena</WorkerName>
                <WorkerSurname>Zelenska</WorkerSurname>
                <BirthYear>1978</BirthYear>
              </Worker>
            </Workers>
          </Viddil>
          <Viddil id ="7777">
            <ViddilName>Viddil_17</ViddilName>
            <Workers>
              <Worker id="1111">
                <WorkerName>SviatoslavNew</WorkerName>
                <WorkerSurname>KonderaNew</WorkerSurname>
                <BirthYear>2012</BirthYear>
              </Worker>
            </Workers>
          </Viddil>
        </Viddils>
      </Upravlinnya>
    </Upravlinnyas>
  </Management>
  <Management id="2222">
    <ManagementName>Management_2</ManagementName>
    <Upravlinnyas>
      <Upravlinnya id="2222">
        <UpravlinnyaName>Upravlinnya_2</UpravlinnyaName>
        <Viddils>
          <Viddil id ="2222">
            <ViddilName>Viddil_2</ViddilName>
            <Workers>
              <Worker id="4444">
                <WorkerName>Nidia</WorkerName>
                <WorkerSurname>Dorofeeva</WorkerSurname>
                <BirthYear>2016</BirthYear>
              </Worker>
              <Worker id="5555">
                <WorkerName>Petro</WorkerName>
                <WorkerSurname>Poroshenko</WorkerSurname>
                <BirthYear>1976</BirthYear>
              </Worker>
            </Workers>
          </Viddil>
        </Viddils>
      </Upravlinnya>
    </Upravlinnyas>
  </Management>
  <Management id="3333">
    <ManagementName>Management_333</ManagementName>
    <Upravlinnyas>
      <Upravlinnya id="3333">
        <UpravlinnyaName>Upravlinnya_222</UpravlinnyaName>
        <Viddils>
          <Viddil id ="3333">
            <ViddilName>Viddil_222</ViddilName>
            <Workers>
              <Worker id="6666">
                <WorkerName>Marsha</WorkerName>
                <WorkerSurname>May</WorkerSurname>
                <BirthYear>1995</BirthYear>
              </Worker>
            </Workers>
          </Viddil>
          <Viddil id ="9999">
            <ViddilName>Viddil_212</ViddilName>
            <Workers>
              <Worker id="1010">
                <WorkerName>MarshaNew</WorkerName>
                <WorkerSurname>MayNew</WorkerSurname>
                <BirthYear>1905</BirthYear>
              </Worker>
            </Workers>
          </Viddil>
        </Viddils>
      </Upravlinnya>
    </Upravlinnyas>
  </Management>
</Managements>

感谢帮助) 我期待着您的解决方案!) 感觉只需要改几行

关键是能够针对您要构建的每一行,查明是否应该为单元格添加一个覆盖其后代行的行跨度,您正试图这样做:

<xsl:if test="position() = 1">

但该测试仅在 Worker 上下文中 运行,因此实际上您的逻辑如下所示:

<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>

这就是为什么只有 Viddil 看起来是正确的,因为逻辑只对 Viddil 是正确的;同样的 Viddil-only 逻辑被应用到 Management 和 Upravlinnya,它看起来是错误的。

在我的版本中,我仍在从 Worker 的上下文中进行测试,但我正在“返回 up 树”到最近的重要祖先,并且问那个祖先是不是第一个。我正在使用 ancestorpreceding-sibiling 轴的组合来做到这一点:

<xsl:variable 
    name="firstUpravlinnya"
    select="count(ancestor::Upravlinnya/preceding-sibling::Upravlinnya) = 0"/>

上面说的是,“作为工人,找到我的 Upravlinnya,我的 Upravlinnya 是我管理的所有 Upravlinnya 中的第一个 Upravlinnya(没有兄弟姐妹 before/preceding 它)?”

我还选择不在嵌套的 for-each 循环中全部执行此操作,这样可以工作,但会变得难以阅读。这利用了 XSLT 的自然模板匹配特性来遍历一棵节点树:

<?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>

外观如下:

这样试试:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/Managements">
    <html>
        <body>
            <table border="1">
                <tr>
                    <th>Management Name</th>
                    <th>Upravlinnya Name</th>
                    <th>Viddil Name</th>
                    <th>Name</th>
                    <th>Surname</th>
                    <th>Birth Year</th>
                </tr>
                <xsl:apply-templates select="Management"/>
            </table>
        </body>
    </html>
</xsl:template> 

<xsl:template match="Management">
    <tr>
        <td rowspan="{count(Upravlinnyas/Upravlinnya) + count(Upravlinnyas/Upravlinnya/Viddils/Viddil) + count(Upravlinnyas/Upravlinnya/Viddils/Viddil/Workers/Worker) + 1}">
            <xsl:value-of select="ManagementName"/>
        </td>   
    </tr>
    <xsl:apply-templates select="Upravlinnyas/Upravlinnya"/>
</xsl:template> 

<xsl:template match="Upravlinnya">
    <tr>
        <td rowspan="{count(Viddils/Viddil) + count(Viddils/Viddil/Workers/Worker) + 1}">
            <xsl:value-of select="UpravlinnyaName"/>
        </td>   
    </tr>
    <xsl:apply-templates select="Viddils/Viddil"/>
</xsl:template> 

<xsl:template match="Viddil">
    <tr>
        <td rowspan="{count(Workers/Worker) + 1}">
            <xsl:value-of select="ViddilName"/>
        </td>   
    </tr>
    <xsl:apply-templates select="Workers/Worker"/>
</xsl:template> 

<xsl:template match="Worker">
    <tr>
        <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>

应用于您的输入示例,这将 return:

结果

<html>
   <body>
      <table border="1">
         <tr>
            <th>Management Name</th>
            <th>Upravlinnya Name</th>
            <th>Viddil Name</th>
            <th>Name</th>
            <th>Surname</th>
            <th>Birth Year</th>
         </tr>
         <tr>
            <td rowspan="8">Management_1</td>
         </tr>
         <tr>
            <td rowspan="7">Upravlinnya_1</td>
         </tr>
         <tr>
            <td rowspan="4">Viddil_1</td>
         </tr>
         <tr>
            <td>Sviatoslav</td>
            <td>Kondera</td>
            <td>2002</td>
         </tr>
         <tr>
            <td>Orest</td>
            <td>Rodcevich</td>
            <td>1999</td>
         </tr>
         <tr>
            <td>Olena</td>
            <td>Zelenska</td>
            <td>1978</td>
         </tr>
         <tr>
            <td rowspan="2">Viddil_17</td>
         </tr>
         <tr>
            <td>SviatoslavNew</td>
            <td>KonderaNew</td>
            <td>2012</td>
         </tr>
         <tr>
            <td rowspan="5">Management_2</td>
         </tr>
         <tr>
            <td rowspan="4">Upravlinnya_2</td>
         </tr>
         <tr>
            <td rowspan="3">Viddil_2</td>
         </tr>
         <tr>
            <td>Nidia</td>
            <td>Dorofeeva</td>
            <td>2016</td>
         </tr>
         <tr>
            <td>Petro</td>
            <td>Poroshenko</td>
            <td>1976</td>
         </tr>
         <tr>
            <td rowspan="6">Management_333</td>
         </tr>
         <tr>
            <td rowspan="5">Upravlinnya_222</td>
         </tr>
         <tr>
            <td rowspan="2">Viddil_222</td>
         </tr>
         <tr>
            <td>Marsha</td>
            <td>May</td>
            <td>1995</td>
         </tr>
         <tr>
            <td rowspan="2">Viddil_212</td>
         </tr>
         <tr>
            <td>MarshaNew</td>
            <td>MayNew</td>
            <td>1905</td>
         </tr>
      </table>
   </body>
</html>

渲染


请注意,这不是严格正确的解决方案。理想情况下,每个部分的第一行应包含 table 中每一列的单元格,同样,每个小节的第一行应包含剩余到右侧的每一列的单元格小节的列。

因此,结果略有偏差:同一行中单元格的上边框没有完全对齐。但是,我相信为如此简单的代码付出的代价很小。