XSLT将排序的节点序列呈现为M x N表

时间:2015-12-02 18:46:25

标签: xml sorting xslt

关于在XSLT中将节点序列渲染为M x N表,存在一个5年前的问题(下面链接)。答案很好,我在几个项目中都使用过它。

Rendering a node sequence as M x N table

我现在遇到需要排序输出的情况。新项目将添加到序列的末尾,我不能简单地重新生成XML。

在我看来,使用此代码作为起点,必须在调用任何模板之前对列表进行排序。我无法提出解决方案,并希望其中一位大师可以提供帮助。

一些补充资料以供澄清。这适用于仅支持XSLT 1.0版的SharePoint 2010网站。我从列表中呈现数据,这些数据可以添加到但不重新排序而不删除和重新输入数据。由于我无法对列表进行重新排序,因此我希望使用XSLT对输出进行排序。

这是输入xml;

<root>
  <CRMData>
      <Rows>
      <Row Name1="1 test customer" Title="ACC-011636"/>
      <Row Name1="7 test customer" Title="ACC-011618"/>
      <Row Name1="14 test customer" Title="ACC-011635"/>
      <Row Name1="6 test customer" Title="ACC-011610"/>
      <Row Name1="22 test customer" Title="ACC-011627"/>
      <Row Name1="12 test customer" Title="ACC-011748"/>
      <Row Name1="3 test customer" Title="ACC-011607"/>
      <Row Name1="9 test customer" Title="ACC-011628"/>
      <Row Name1="45 test customer" Title="ACC-011754"/>
      <Row Name1="16 test customer" Title="ACC-011774"/>
      <Row Name1="10 test customer" Title="ACC-011632"/>
      <Row Name1="7 test customer" Title="ACC-011606"/>
      <Row Name1="19 test customer" Title="ACC-012275"/>
      <Row Name1="59 test customer" Title="ACC-011634"/>
      <Row Name1="33 test customer" Title="NONE-001"/>
      <Row Name1="2 test customer" Title="ACC-011617"/>
      <Row Name1="64 test customer" Title="ACC-011629"/>
      <Row Name1="15 test customer" Title="ACC-011633"/>
      <Row Name1="26 test customer" Title="ACC-011612"/>
      <Row Name1="38 test customer" Title="ACC-011608"/>
      <Row Name1="21 test customer" Title="ACC-011749"/>
      <Row Name1="8 test customer" Title="ACC-011611"/>
      <Row Name1="17 test customer" Title="ACC-011613"/>
      <Row Name1="20 test customer" Title="ACC-011714"/>
      <Row Name1="4 test customer" Title="ACC-011616"/>
      <Row Name1="62 test customer" Title="ACC-011601"/>
      <Row Name1="11 test customer" Title="ACC-012918"/>
    </Rows>
  </CRMData>
  <KenData>
    <Rows>
      <Row Title="KEN-3336" Date1="12/15/2009" Date2="6/23/2014" Version="10.1" CustomerID="ACC-011636"/>
      <Row Title="KEN-3338" Date1="10/30/2006" Date2="8/9/2012" Version="8.4.5" CustomerID="ACC-011618"/>
      <Row Title="KEN-3337" Date1="2/10/2014" Date2="2/10/2014" Version="9.3" CustomerID="ACC-011635"/>
      <Row Title="Ken-3339" Date1="3/10/2010" Date2="6/13/2013" Version="10" CustomerID="ACC-011610"/>
      <Row Title="KEN-3340" Date1="11/22/2010" Date2="9/24/2014" Version="10.1" CustomerID="ACC-011627"/>
      <Row Title="KEN-3341" Date1="1/21/2013" Date2="1/30/2015" Version="10.3" CustomerID="ACC-011748"/>
      <Row Title="KEN-3342" Date1="10/1/2008" Date2="10/1/2008" Version="8.4" CustomerID="ACC-011607"/>
      <Row Title="KEN-3344" Date1="6/17/2008" Date2="6/17/2008" Version="9.2" CustomerID="ACC-011628"/>
      <Row Title="KEN-3345" Date1="12/23/2008" Date2="11/25/2014" Version="10.2" CustomerID="ACC-011754"/>
      <Row Title="KEN-3347" Date1="11/17/2010" Date2="11/17/2010" Version="8.4.5" CustomerID="ACC-011774"/>
      <Row Title="KEN-3349" Date1="Pending" Date2="Pending" Version="10.1" CustomerID="ACC-011632"/>
      <Row Title="KEN-3350" Date1="4/2/2012" Date2="1/17/2012" Version="8.4.5" CustomerID="ACC-011606"/>
      <Row Title="KEN-3351" Date1="8/10/2015" Date2="8/10/2015" Version="10.3" CustomerID="ACC-012275"/>
      <Row Title="KEN-3353" Date1="Pending" Date2="Pending" Version="9.3" CustomerID="ACC-011634"/>
      <Row Title="KEN-3346" Date1="3/7/2011" Date2="3/15/2011" Version="8.4.5" CustomerID="NONE-001"/>
      <Row Title="KEN-3354" Date1="7/2/2013" Date2="10/12/2015" Version="10.3" CustomerID="ACC-011617"/>
      <Row Title="KEN-3355" Date1="8/15/2013" Date2="8/15/2013" Version="9.3" CustomerID="ACC-011629"/>
      <Row Title="KEN-3356" Date1="8/18/2014" Date2="8/18/2014" Version="9.3" CustomerID="ACC-011633"/>
      <Row Title="KEN-3357" Date1="3/25/2003" Date2="10/18/2011" Version="8.4" CustomerID="ACC-011612"/>
      <Row Title="KEN-3358" Date1="9/15/2007" Date2="11/18/2014" Version="10.2" CustomerID="ACC-011608"/>
      <Row Title="KEN-3359" Date1="8/1/2006" Date2="6/1/2015" Version="10.3" CustomerID="ACC-011749"/>
      <Row Title="KEN-3360" Date1="9/20/2010" Date2="9/20/2010" Version="8.4" CustomerID="ACC-011611"/>
      <Row Title="KEN-3361" Date1="7/14/2014" Date2="8/5/2014" Version="10.2" CustomerID="ACC-011613"/>
      <Row Title="KEN-3362" Date1="1/20/2005" Date2="8/21/2012" Version="9.2" CustomerID="ACC-011714"/>
      <Row Title="KEN-3363" Date1="3/15/2007" Date2="4/25/2008" Version="8.4" CustomerID="ACC-011616"/>
      <Row Title="KEN-3364" Date1="7/10/2014" Date2="7/10/2014" Version="10" CustomerID="ACC-011601"/>
      <Row Title="KEN-3875" Date1="7/10/2015" Date2="7/10/2015" Version="10.3" CustomerID="ACC-012918"/>
    </Rows>
  </KenData>
  <OtherData>
    <Rows>
      <Row Title="OTH-001" CustomerID="ACC-011636" Data1="Yes" Data2="Yes" Data3="No"/>
      <Row Title="OTH-002" CustomerID="ACC-011618" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-003" CustomerID="ACC-011635" Data1="No" Data2="Yes" Data3="No"/>
      <Row Title="OTH-004" CustomerID="ACC-011610" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-005" CustomerID="ACC-011627" Data1="No" Data2="No" Data3="Yes"/>
      <Row Title="OTH-006" CustomerID="ACC-011748" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-007" CustomerID="ACC-011607" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-008" CustomerID="ACC-011628" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-009" CustomerID="ACC-011754" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-010" CustomerID="ACC-011774" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-011" CustomerID="ACC-011632" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-012" CustomerID="ACC-011606" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-013" CustomerID="ACC-012275" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-014" CustomerID="ACC-011634" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-015" CustomerID="NONE-001"   Data1="No" Data2="No" Data3="Yes"/>
      <Row Title="OTH-016" CustomerID="ACC-011617" Data1="Yes" Data2="No" Data3="Yes"/>
      <Row Title="OTH-017" CustomerID="ACC-011629" Data1="No" Data2="No" Data3="Yes"/>
      <Row Title="OTH-018" CustomerID="ACC-011633" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-019" CustomerID="ACC-011612" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-020" CustomerID="ACC-011608" Data1="No" Data2="Yes" Data3="No"/>
      <Row Title="OTH-021" CustomerID="ACC-011749" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-022" CustomerID="ACC-011611" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-023" CustomerID="ACC-011613" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-024" CustomerID="ACC-011714" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-025" CustomerID="ACC-011616" Data1="Yes" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-026" CustomerID="ACC-011601" Data1="No" Data2="Yes" Data3="Yes"/>
      <Row Title="OTH-027" CustomerID="ACC-012918" Data1="No" Data2="Yes" Data3="Yes"/>
    </Rows>
  </OtherData>
  <NPSData>
    <Rows>
      <Row Title="NPS-0001" CustomerID="ACC-011636" Type1="LDS" Score="4.00" Year="2014"/>
      <Row Title="NPS-0002" CustomerID="ACC-011636" Type1="LDS" Score="6.00" Year="2015"/>
      <Row Title="NPS-0003" CustomerID="ACC-011636" Type1="PS" Score="3.00" Year="2014"/>
      <Row Title="NPS-0004" CustomerID="ACC-011618" Type1="LDS" Score="7.00" Year="2014"/>
      <Row Title="NPS-0005" CustomerID="ACC-011618" Type1="LDS" Score="8.00" Year="2015"/>
      <Row Title="NPS-0006" CustomerID="ACC-011635" Type1="LDS" Score="6.00" Year="2014"/>
      <Row Title="NPS-0007" CustomerID="ACC-011635" Type1="LDS" Score="2.50" Year="2015"/>
      <Row Title="NPS-0008" CustomerID="ACC-011610" Type1="LDS" Score="7.50" Year="2014"/>
      <Row Title="NPS-0009" CustomerID="ACC-011610" Type1="LDS" Score="7.50" Year="2015"/>
      <Row Title="NPS-0010" CustomerID="ACC-011610" Type1="PS" Score="7.67" Year="2015"/>
      <Row Title="NPS-0011" CustomerID="ACC-011627" Type1="LDS" Score="5.50" Year="2014"/>
      <Row Title="NPS-0012" CustomerID="ACC-011627" Type1="LDS" Score="8.67" Year="2015"/>
      <Row Title="NPS-0013" CustomerID="ACC-011748" Type1="LDS" Score="7.00" Year="2014"/>
      <Row Title="NPS-0014" CustomerID="ACC-011748" Type1="LDS" Score="8.00" Year="2015"/>
      <Row Title="NPS-0015" CustomerID="ACC-011748" Type1="PS" Score="6.00" Year="2015"/>
      <Row Title="NPS-0016" CustomerID="ACC-011628" Type1="LDS" Score="7.00" Year="2015"/>
      <Row Title="NPS-0017" CustomerID="ACC-011754" Type1="LDS" Score="7.50" Year="2015"/>
      <Row Title="NPS-0018" CustomerID="ACC-011754" Type1="PS" Score="5.00" Year="2014"/>
      <Row Title="NPS-0019" CustomerID="ACC-011774" Type1="LDS" Score="7.50" Year="2014"/>
      <Row Title="NPS-0020" CustomerID="ACC-011606" Type1="LDS" Score="8.00" Year="2014"/>
      <Row Title="NPS-0021" CustomerID="ACC-012275" Type1="LDS" Score="8.00" Year="2014"/>
      <Row Title="NPS-0022" CustomerID="ACC-012275" Type1="LDS" Score="7.00" Year="2015"/>
      <Row Title="NPS-0023" CustomerID="ACC-011634" Type1="LDS" Score="3.67" Year="2014"/>
      <Row Title="NPS-0024" CustomerID="ACC-011617" Type1="LDS" Score="10.0" Year="2014"/>
      <Row Title="NPS-0025" CustomerID="ACC-011617" Type1="LDS" Score="10.0" Year="2015"/>
      <Row Title="NPS-0026" CustomerID="ACC-011629" Type1="LDS" Score="8.00" Year="2014"/>
      <Row Title="NPS-0027" CustomerID="ACC-011629" Type1="LDS" Score="8.00" Year="2015"/>
      <Row Title="NPS-0028" CustomerID="ACC-011633" Type1="LDS" Score="7.67" Year="2014"/>
      <Row Title="NPS-0029" CustomerID="ACC-011633" Type1="LDS" Score="7.50" Year="2015"/>
      <Row Title="NPS-0030" CustomerID="ACC-011633" Type1="PS" Score="7.00" Year="2014"/>
      <Row Title="NPS-0031" CustomerID="ACC-011612" Type1="LDS" Score="6.00" Year="2014"/>
    </Rows>
  </NPSData>
</root>

我希望能够根据CRMData的Name1字段对列表进行排序。

我使用的样式表是;

<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">

  <xsl:output method="html" indent="no"/>
  <!--<xsl:strip-space elements="*"/> SharePoint doesn't like this -->
  <xsl:decimal-format NaN=""/>
  <xsl:param name="dvt_apos">&apos;</xsl:param>
  <xsl:param name="ManualRefresh"></xsl:param>
  <xsl:variable name="dvt_1_automode">0</xsl:variable>

  <xsl:variable name="perRow" select="3" />

  <xsl:template match="/root/CRMData/Rows" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
    <table>
      <xsl:apply-templates
        mode   = "tr"
        select = "Row[position() mod $perRow = 1]" />
    </table>
  </xsl:template>

  <xsl:template match="Row" mode="tr">
    <tr>
      <xsl:call-template name="blank" />
      <xsl:variable name="td" select=". | following-sibling::Row[position() &lt; $perRow]" />
      <xsl:apply-templates mode="td" select="$td" />
      <xsl:if test="count($td) &lt; $perRow">
        <xsl:call-template name="filler">
          <xsl:with-param name="rest" select="$perRow - count($td)" />
        </xsl:call-template>
      </xsl:if>
      <xsl:call-template name="blank" />
    </tr>
  </xsl:template>

  <xsl:template match="Row" mode="td">
    <xsl:variable name="id" select="@Title" />
    <xsl:variable name="Kendata" select="/root/KenData/Rows/Row[@CustomerID=$id]" />
    <xsl:variable name="Otherdata" select="/root/OtherData/Rows/Row[@CustomerID=$id]" />
    <xsl:variable name="NPSdata" select="/root/NPSData/Rows/Row[@CustomerID=$id and @Type1='LDS' and @Year='2015']" />

    <xsl:variable name="bgcolor">
      <xsl:choose>
        <xsl:when test="$NPSdata/@Score &gt;'7.0'">
          <xsl:value-of select="'#00ff00'" />
        </xsl:when>
        <xsl:when test="$NPSdata/@Score &gt;'5.0'">
          <xsl:value-of select="'#ffff00'" />
        </xsl:when>
        <xsl:when test="$NPSdata/@Score &gt;'0'">
          <xsl:value-of select="'#ff0000'" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="'#ffffff'" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <td valign="top">
      <table frame="box" style="border-collapse: collapse">
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px;text-align:center">
            <xsl:value-of select="@Name1"/>
          </td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px;text-align:center;background-color:{$bgcolor}">NPS -
            <xsl:value-of select="$NPSdata/@Score"/>
          </td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px;text-align:center">Data1 -
            <xsl:value-of select="$Otherdata/@Data1"/>
          </td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px;text-align:center">Data2 -
            <xsl:value-of select="$Otherdata/@Data2"/>
          </td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px;text-align:center">
            <xsl:value-of select="$Kendata/@Version"/>
-
            <xsl:value-of select="$Kendata/@Date1"/>
          </td>
        </tr>
      </table>
    </td>
  </xsl:template>

  <xsl:template name="filler">
    <xsl:param name="rest" select="0" />
    <xsl:if test="$rest">
      <xsl:call-template name="blank" />
      <xsl:call-template name="filler">
        <xsl:with-param name="rest" select="$rest - 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="blank">
    <td valign="top">
      <table style="border-collapse: collapse">
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
        <tr>
          <td class="ms-rteTableOddCol-0" style="width:335px"></td>
        </tr>
      </table>
    </td>
  </xsl:template>

</xsl:stylesheet>

这产生输出为;

1测试客户7测试客户14测试客户

6测试客户22测试客户12测试客户

我希望将输出分类为生成;

1测试客户2测试客户3测试客户

4测试客户5测试客户6测试客户

提前致谢

2 个答案:

答案 0 :(得分:0)

您可以使用xsl:sort和micro-pipe-lining来实现排序表格化。例如,如果输入文档是......

<recordset name="resId" >
  <record n="1">B</record>
  <record n="2">C</record>
  <record n="0">A</record>
  <record n="4">E</record>
  <record n="3">D</record>  
</recordset>

...应用此XSLT 2.0样式表...

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:variable name="num-cols" select="2" />

<xsl:template match="/">
  <hmtl>
    <head>
      <title>Records in tabular format</title>
    </head>
    <body>
      <h1>Records in a <xsl:value-of select="$num-cols"/> table</h1>
      <xsl:apply-templates/>
    </body>  
  </hmtl>
</xsl:template>

<xsl:template match="recordset">
  <table>
    <thead>
      <tr>
        <xsl:for-each select="1 to $num-cols">
          <td>record</td>
        </xsl:for-each>
      </tr>    
    </thead>
    <tbody>

      <xsl:variable name="records">
        <!-- Hold the records in a variable, so we can sort them. -->
        <xsl:apply-templates select="record" mode="copy">
          <xsl:sort select="@n" data-type="number" stable="yes" />
        </xsl:apply-templates>
      </xsl:variable>

      <xsl:for-each-group select="$records/record" group-adjacent="count(preceding-sibling::*) idiv $num-cols">
        <!-- Normal tabularisation technique. Our data is already sorted. -->
        <tr>
          <xsl:apply-templates select="current-group()" />
          <xsl:for-each select="count(current-group()) + 1 to $num-cols">
            <!-- The input document is ragged. So fill in the empty cells.  -->
            <td>&#160;</td>
          </xsl:for-each>
        </tr>
      </xsl:for-each-group>
    </tbody>
  </table>
</xsl:template>

<xsl:template match="record" mode="copy">
  <xsl:copy-of select="." />
</xsl:template>

<xsl:template match="record">
  <td>
    <xsl:value-of select="." />
  </td>
</xsl:template>

</xsl:transform>

...将产生此输出html页面......

<!DOCTYPE HTML>
<hmtl>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>Records in tabular format</title>
   </head>
   <body>
      <h1>Records in a 2 table</h1>
      <table>
         <thead>
            <tr>
               <td>record</td>
               <td>record</td>
            </tr>
         </thead>
         <tbody>
            <tr>
               <td>A</td>
               <td>B</td>
            </tr>
            <tr>
               <td>C</td>
               <td>D</td>
            </tr>
            <tr>
               <td>E</td>
               <td>&nbsp;</td>
            </tr>
         </tbody>
      </table>
   </body>
</hmtl>

答案 1 :(得分:0)

简短回答:您需要在两次通过中执行此操作。首先对记录进行排序,然后将它们分成行和列。

现在,为了简化手头问题的示例,请考虑以下样式表:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:param name="columns" select="3"/>

<xsl:template match="/root">
    <!-- first pass -->
    <xsl:variable name="sorted-rows">
        <xsl:for-each select="CRMData/Rows/Row">
            <xsl:sort select="@Name1" data-type="text" order="ascending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:variable>
    <!-- output -->
    <table border="1">
        <xsl:for-each select="exsl:node-set($sorted-rows)/Row[position() mod $columns = 1]" >
            <tr>
                <xsl:apply-templates select=". | following-sibling::Row[position() &lt; $columns]"/>
            </tr>
        </xsl:for-each>
    </table>
</xsl:template>

<xsl:template match="Row">
    <td>
        <xsl:value-of select="@Name1"/>
    </td>
</xsl:template>

</xsl:stylesheet>

应用于您的示例输入,此处的结果将如下所示:

enter image description here

请注意,这与您发布的预期输出略有不同,因为记录按字母顺序排序 - 因此19位于2之前。我认为正确的顺序,并且您的实际数据实际上并不包含数字前缀。否则,您必须将排序指令更改为:

<xsl:sort select="substring-before(@Name1, ' ')" data-type="number" order="ascending"/> 

与您的问题无关:我建议您使用 keys 从其他行组中获取数据。