对XML数据进行排序并应用XSLT转换

时间:2013-10-23 02:00:30

标签: xml xslt xslt-1.0

给出以下XSLT和XML:

XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
                >
  <xsl:template match="/*">
    <html>
      <head>
        <style type="text/css">
          .exceed{background:yellow;}
          table{border-collapse:collapse;
          cellspacing:0px;
          cellpadding:5px;
          width:90%;}
          .total-hours{background-color:#808080;}
          .total-row-text{font-weight:bold;} 
          .report-header{background-color:#C6BD94;}
          /*{background-color:#FFFFBB;

          }*/
          .Report { width: 90%; font-family:Cambria,serif; font-size:14px  }
          .Report th { border: solid 1px }
          .Report td { padding:5px; padding-left:20px; padding-right:20px; border: solid 1px }
          tr{background-color:#FFFFBB;}
        </style>
      </head>

      <body>
        <div class='ReportDescription'>
        </div>
        <div class='ReportDate'>
        </div>
        <table class="Report">
          <tr class="report-header">
            <xsl:for-each select="headers/header">

              <th class="report-Header">
                <xsl:value-of select="title"/>
              </th>
            </xsl:for-each>
          </tr>
          <xsl:apply-templates mode="Gray" select="PreviousDays/Employee[@total]">
          </xsl:apply-templates>
          <xsl:apply-templates mode="normal" select="PreviousDays/Employee[not(@total)]">
          </xsl:apply-templates>
        </table>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="PreviousDays/Employee" mode="Gray">       
    <tr class="total-hours">
      <td class="total-row-text">
        <xsl:value-of select="WorkingDate"/>
      </td>
      <td class="total-row-text">
        <xsl:value-of select="@Name"/>
      </td>

      <td class="total-row-text">
        <xsl:value-of select="totalhours"/>
      </td>
      <td class="total-row-text">
        <xsl:value-of select="Description"/>
      </td>
    </tr>

  </xsl:template>
  <xsl:template mode="normal" match="PreviousDays/Employee">

    <tr class="exceed">
      <td>
        <xsl:value-of select="WorkingDate"/>
      </td>
      <td>
        <xsl:value-of select="@Name"/>
      </td>

      <td>
        <xsl:value-of select="HoursOnProject"/>
      </td>         
      <td>
        <xsl:value-of select="Department"/>
      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

XML

<root>
  <headers>
    <header><title>Header1</title></header>
    <header><title>Header2</title></header>
    <header><title>Header3</title></header>
    <header><title>Header4</title></header>
  </headers>
  <PreviousDays>
    <Employee  Name="Martin Davis">
      <WorkingDate>9/12/2013</WorkingDate>
      <HoursOnProject>8</HoursOnProject>
      <Description>Description here</Description>
    </Employee>
    <Employee total="true" Name="Martin Davis">
      <WorkingDate>9/12/2013</WorkingDate>
      <HoursOnProject></HoursOnProject>
      <Description>Description here</Description>
      <totalhours>8</totalhours>
    </Employee>
    <Employee Name="Caroline Jackson">
      <WorkingDate>9/15/2013</WorkingDate>
      <HoursOnProject>8.50</HoursOnProject>
      <Description>Description here</Description>
    </Employee>
    <Employee total="true" Name="Caroline Jackson">
      <WorkingDate>9/15/2013</WorkingDate>
      <HoursOnProject></HoursOnProject>
      <Description>Description here</Description>
      <totalhours>10</totalhours>
    </Employee>
  </PreviousDays>
</root>

如何确保通过name属性和WorkingDate对数据进行排序或分组,以便每个单独记录的总行直接显示在任何正常行之后?最好不使用for-each或if type语句。 (不过,欢迎使用这些类型的解决方案。)

期望的输出

<table class="Report">
      <tr class="report-header">
        <th class="report-Header">Header1</th>
        <th class="report-Header">Header2</th>
        <th class="report-Header">Header3</th>
        <th class="report-Header">Header4</th>
      </tr>
      <tr class="exceed">
        <td>9/12/2013</td>
        <td>Martin Davis</td>
        <td>8</td>
        <td></td>
      </tr>
      <tr class="total-hours">
        <td class="total-row-text">9/12/2013</td>
        <td class="total-row-text">Martin Davis</td>
        <td class="total-row-text">8</td>
        <td class="total-row-text">Description here</td>
      </tr>
      <tr class="exceed">
        <td>9/15/2013</td>
        <td>Caroline Jackson</td>
        <td>8.50</td>
        <td></td>
      </tr>
      <tr class="total-hours">
        <td class="total-row-text">9/15/2013</td>
        <td class="total-row-text">Caroline Jackson</td>
        <td class="total-row-text">10</td>
        <td class="total-row-text">Description here</td>
      </tr>    
    </table>

请注意,如果可以避免,我不想切换到 XSLT 2.0

1 个答案:

答案 0 :(得分:0)

你可以尝试这个xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

<!-- Employee without @total grouped by Name  -->
<xsl:key name="employees-by-name" match="/root/PreviousDays/Employee[not(@total)]" use="@Name" />
<!-- Employee with @total grouped by Name  -->
<xsl:key name="employees-total-by-name" match="/root/PreviousDays/Employee[@total]" use="@Name" />

<!-- templates for writting the trs -->
<xsl:template match="Employee" mode="css-class">
  <tr class="exceed">
    <xsl:apply-templates select="." mode="contents" />
  </tr>
</xsl:template>
<xsl:template match="Employee[@total='true']" mode="css-class">
  <tr class="total-hours">
    <xsl:apply-templates select="." mode="contents" />
  </tr>
</xsl:template>

<!-- template for writting the tds -->
<xsl:template match="Employee" mode="contents">
  <td><xsl:value-of select="WorkingDate"/></td>
  <td><xsl:value-of select="@Name"/></td>
  <td><xsl:value-of select="HoursOnProject"/></td>         
  <td><xsl:value-of select="Department"/></td>
</xsl:template>


<xsl:template match="/root">
  <html>
    <head>
      <style type="text/css">
      .exceed{background:yellow;}
      table{border-collapse:collapse;cellspacing:0px;cellpadding:5px;width:90%;}
      .total-hours td{background-color:#808080;font-weight:bold;}
      .report-header{background-color:#C6BD94;}
      .Report {width: 90%; font-family:Cambria,serif; font-size:14px;}
      .Report th {border: solid 1px;}
      .Report td {padding:5px; padding-left:20px; padding-right:20px; border: solid 1px;}
      tr{background-color:#FFFFBB;}
      </style>
    </head>
    <body>
      <div class='ReportDescription'>
      </div>
      <div class='ReportDate'>
      </div>
      <table class="Report">
        <tr class="report-header">
          <xsl:for-each select="headers/header">
            <th class="report-Header">
              <xsl:value-of select="title"/>
            </th>
          </xsl:for-each>
        </tr>
        <!-- loop for each Employee which does not have a preceding-sibling with same @Name -->
        <xsl:for-each select="PreviousDays/Employee[not(@Name = preceding-sibling::Employee/@Name)]">
          <!-- apply templates for those elements with same @Name and not @total attribute -->
          <xsl:apply-templates select="key('employees-by-name',@Name)" mode="css-class">
            <xsl:sort select="WorkingDate" />
          </xsl:apply-templates>
          <!-- apply templates for those -or that- elements with same @Name and with @total attribute -->
          <xsl:apply-templates select="key('employees-total-by-name',@Name)" mode="css-class" />
        </xsl:for-each>
      </table>
    </body>
  </html>
</xsl:template>

</xsl:stylesheet>