2

我想在 3 列中输出分组结果。我没有使用表格,所以我想我的意思是并排显示 3 个结果部分(参见 HTML)。

我根据其中一个节点的值对结果进行了分组,并在一个标题下分组,该标题是节点的值。例如:

<All_Results>
  <Result>
   <Dept>Finance</Dept>
   <Name>Bob</Name>
  </Result>
  <Result>
   <Dept>Finance</Dept>
   <Name>Susan</Name>
  </Result>
  <Result>
   <Dept>Sales</Dept>
   <Name>Igor</Name>
  </Result>
</All_Results>

格式如下:

 <li>
    <h4>Finance</h4>
    <ul>
      <li>Bob</li>
    </ul>
    <ul>
      <li>Susan</li>
    </ul>

    <h4>Sales</h4>
    <ul>
      <li>Igor</li>
    </ul>
 </li>

这很有效,我很满意。现在我要做的是并排创建 3 列 Depts 及其结果(即本例中的 Names)。我期待 9 个可能的部门,但这可能会在未来发生变化。

到目前为止,这是我的 XSLT(到目前为止,它所做的只是上面的格式,还没有在列上工作,我不知道如何解决这个问题):

<xsl:output method="html" />
<xsl:key name="results-by-dept" match="Result" use="Dept" />
<xsl:template match="All_Results">
  <xsl:variable name="Rows" select="Result" />
  <xsl:variable name="RowCount" select="count($Rows)" />
  <ul class="deptList">
    <xsl:for-each select="Result[count(. | key('results-by-dept', Dept)[1]) = 1]">
      <xsl:sort select="Dept"  order ="ascending"/>
      <li>
        <h4>
          <xsl:value-of select="Dept" />
        </h4>
        <ul>
          <xsl:for-each select="key('results-by-dept', Dept)">
            <xsl:sort select="Name" />
            <li>
                <xsl:value-of select="Name"/>
            </li>
          </xsl:for-each>
        </ul>
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>
</xsl:stylesheet>

任何帮助将非常感激!


肖恩的编辑

在与 OP 讨论之后,我认为这个用例说明了他想要什么。以下示例输入文档显示了 5 个部门,有 1 名或 2 名员工。节点的顺序并不重要。

用例 1:输入文档

<All_Results>
    <Result>
        <Dept>Finance</Dept>
        <Name>Bob</Name>
    </Result>
    <Result>
        <Dept>Engineering</Dept>
        <Name>Inna</Name>
    </Result>
    <Result>
        <Dept>Finance</Dept>
        <Name>Susan</Name>
    </Result>
    <Result>
        <Dept>Sales</Dept>
        <Name>Igor</Name>
    </Result>
    <Result>
        <Dept>Human resources</Dept>
        <Name>Jane</Name>
    </Result>
    <Result>
        <Dept>Admin</Dept>
        <Name>Joe</Name>
    </Result>
    <Result>
        <Dept>Engineering</Dept>
        <Name>Dima</Name>
    </Result>
    <Result>
        <Dept>Human resources</Dept>
        <Name>Beth</Name>
    </Result>
</All_Results>

输出将像这样进行转换(如下列表)。HTML 表格将由 3 列但只有 1 行构成。每个单元格都包含一个以部门名称为首的部门员工的 HTML 无序列表。每列包含大约三分之一的部门,最后一列在这方面参差不齐。从上到下然后从左到右阅读,部门应该按字母顺序排序。在每个部门内,员工应按字母顺序排列。

用例 1:输出文档

<table>
    <tr>
        <td>
            <ul class="deptList">
                <li>
                    <h4>Admin</h4>
                    <ul>
                        <li>Joe</li>
                    </ul>
                </li>
                <li>
                    <h4>Engineering</h4>
                    <ul>
                        <li>Dima</li>
                        <li>Inna</li>
                    </ul>
                </li>
            </ul>
        </td>
        <td>
            <ul class="deptList">
                <li>
                    <h4>Finance</h4>
                    <ul>
                        <li>Bob</li>
                        <li>Susan</li>
                    </ul>
                </li>
                <li>
                    <h4>Human resources</h4>
                    <ul>
                        <li>Beth</li>
                        <li>Jane</li>
                    </ul>
                </li>
            </ul>
        </td>
        <td>
            <ul class="deptList">
                <li>
                    <h4>Sales</h4>
                    <ul>
                        <li>Igor</li>
                    </ul>
                </li>
            </ul>
        </td>
    </tr>
</table>
4

3 回答 3

2

这个 XSLT 1.0 转换

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

 <xsl:key name="kDeptByVal" match="Dept" use="."/>
 <xsl:key name="kEmpByDept" match="Name" use="../Dept"/>

 <xsl:variable name="vDeptsUniq" select=
  "/*/*/Dept[generate-id() = generate-id(key('kDeptByVal',.)[1])]"/>

 <xsl:variable name="vDeptsPerCol" select="ceiling(count($vDeptsUniq) div 3)"/>

 <xsl:template match="/*">
  <table border="1">
    <tr>
      <xsl:for-each select="$vDeptsUniq[not(position() > 3)]">
        <xsl:variable name="vCol" select="position()"/>
        <td>
          <ul class="deptList">
           <xsl:for-each select="$vDeptsUniq">
            <xsl:sort/>
            <xsl:if test=
             "position() > ($vCol -1)*$vDeptsPerCol
             and
              not(position() > $vCol*$vDeptsPerCol)
             ">
                <li>
                      <h4><xsl:value-of select="."/></h4>
                    <ul>
                      <xsl:apply-templates select="key('kEmpByDept', .)">
                       <xsl:sort/>
                      </xsl:apply-templates>
                    </ul>
                </li>
            </xsl:if>
           </xsl:for-each>
          </ul>
        </td>
      </xsl:for-each>
    </tr>
  </table>
 </xsl:template>

 <xsl:template match="Name">
  <li><xsl:value-of select="."/></li>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<All_Results>
    <Result>
        <Dept>Finance</Dept>
        <Name>Bob</Name>
    </Result>
    <Result>
        <Dept>Engineering</Dept>
        <Name>Inna</Name>
    </Result>
    <Result>
        <Dept>Finance</Dept>
        <Name>Susan</Name>
    </Result>
    <Result>
        <Dept>Sales</Dept>
        <Name>Igor</Name>
    </Result>
    <Result>
        <Dept>Human resources</Dept>
        <Name>Jane</Name>
    </Result>
    <Result>
        <Dept>Admin</Dept>
        <Name>Joe</Name>
    </Result>
    <Result>
        <Dept>Engineering</Dept>
        <Name>Dima</Name>
    </Result>
    <Result>
        <Dept>Human resources</Dept>
        <Name>Beth</Name>
    </Result>
</All_Results>

产生想要的正确结果

<table border="1">
   <tr>
      <td>
         <ul class="deptList">
            <li>
               <h4>Admin</h4>
               <ul>
                  <li>Joe</li>
               </ul>
            </li>
            <li>
               <h4>Engineering</h4>
               <ul>
                  <li>Dima</li>
                  <li>Inna</li>
               </ul>
            </li>
         </ul>
      </td>
      <td>
         <ul class="deptList">
            <li>
               <h4>Finance</h4>
               <ul>
                  <li>Bob</li>
                  <li>Susan</li>
               </ul>
            </li>
            <li>
               <h4>Human resources</h4>
               <ul>
                  <li>Beth</li>
                  <li>Jane</li>
               </ul>
            </li>
         </ul>
      </td>
      <td>
         <ul class="deptList">
            <li>
               <h4>Sales</h4>
               <ul>
                  <li>Igor</li>
               </ul>
            </li>
         </ul>
      </td>
   </tr>
</table>
于 2012-08-01T04:05:17.233 回答
1

这是 XSLT 1.0 中的一种方法:

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

  <xsl:key name="depts" match="Dept" use="." />
  <xsl:key name="results-by-dept" match="Result" use="Dept" />

  <xsl:template match="All_Results">
    <xsl:variable name="max-cols" select="3"/>
    <xsl:variable name="depts"
                  select="Result/Dept[count(.|key('depts', .)[1]) = 1]"/>
    <xsl:variable name="col-size"
                  select="ceiling(count($depts) div $max-cols)"/>
    <table>
      <tr>
        <xsl:for-each select="$depts[(position() - 1) mod $col-size = 0]">
          <xsl:variable name="col" select="position() - 1"/>
          <td>
            <ul class="deptList">
              <xsl:for-each select="$depts">
                <xsl:sort select="."/>
                <xsl:if test="floor((position() - 1) div $col-size) = $col">
                  <li>
                    <xsl:apply-templates select="."/>
                  </li>
                </xsl:if>
              </xsl:for-each>
            </ul>
          </td>
        </xsl:for-each>
      </tr>
    </table>
  </xsl:template>


  <xsl:template match="Dept">
    <h4>
      <xsl:value-of select="." />
    </h4>
    <ul>
      <xsl:for-each select="key('results-by-dept', .)">
        <xsl:sort select="Name" />
        <li>
          <xsl:value-of select="Name"/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

</xsl:stylesheet>

一些注意事项:

  • 这里的主要技巧是使用$depts[(position() - 1) mod $col-size = 0]循环尽可能多的列。
  • 循环遍历列的另一种方法是使用递归模板并增加计数器变量。
  • 这在 XSLT 2.0 中会容易得多。
  • Muenchian 分组中的count(.|key('depts', .)[1]) = 1谓词可以等效地写为。我更喜欢后者,因为我认为它不那么晦涩难懂,但我在这里使用了前者,因为它是问题中使用的。generate-id() = generate-id(key('depts', .)[1])
于 2012-07-31T20:25:52.267 回答
1

这个 XSLT 1.0 样式表将为您提供 3 列...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="kDepartments" match="Result" use="Dept" />

<xsl:template match="/">
 <table>
  <tr>
   <xsl:variable name="dept-count" select="count( */Result[
           generate-id(.) = generate-id( key('kDepartments',Dept)[1])])" />
   <xsl:for-each select="(//*)[position() &lt;= 3]">
     <xsl:variable name="column" select="position()" />
     <td>
       <ul class="deptList">
         <xsl:for-each select="/*/Result[
           generate-id(.) = generate-id( key('kDepartments',Dept)[1])]" >
            <xsl:sort select="Dept" />
            <xsl:variable name="DeptIndex" select="position()" /> 
            <xsl:apply-templates select="self::Result[
              (floor((($DeptIndex - 1)*3 div $dept-count)) + 1) = $column]" /> 
         </xsl:for-each>    
       </ul>  
     </td>  
   </xsl:for-each>  
  </tr>
 </table>
</xsl:template>

<xsl:template match="Result">
 <h4>
  <xsl:value-of select="Dept" />
 </h4>
    <ul>
      <xsl:for-each select="key('kDepartments', Dept)">
        <xsl:sort select="Name" />
        <li>
          <xsl:value-of select="Name"/>
        </li>
      </xsl:for-each>
    </ul>
</xsl:template>

</xsl:stylesheet>
于 2012-08-01T01:08:42.680 回答