按类似标签对XSL模板进行排序

时间:2016-06-02 16:08:43

标签: xml sorting xslt

我正在尝试将从数据库中读取的旅行行程输出为PDF。 Retreiving等等都很好,但我想按日期订购行程。

目前xml结构如下

    <flights>
      <flDate>13OCT</flDate>
      <flFrom>Glasgow International </flFrom>
      <flTo>Philadelphia, PA - International </flTo>
      <flNumber>US769</flNumber>
      <depTime>1020</depTime>
      <arrTime>1300</arrTime>
    </flights>
    <flights>
      <flDate>22OCT</flDate>
      <flFrom>Philadelphia, PA - International </flFrom>
      <flTo>Glasgow International </flTo>
      <flNumber>US768</flNumber>
      <depTime>1855</depTime>
      <arrTime>1830</arrTime>
    </flights>
    <accommodation>
      <accDate>14OCT</accDate>
      <accName>Hotel Los Jameos</accName>
      <duration>10</duration>
      <roomType>Type B Rooms</roomType>
      <boardBasis>H/B</boardBasis>
    </accommodation>

目前的输出是

  13OCT Glasgow to Phili
  ..........
  22OCT Phili to Glasgow
  ..........
  14OCT Hotel Los Jameos
  ..........

显然这不理想,导致混乱。理想情况下,我希望住宿(以及任何其他行程项目)的日期顺序正确。

我该怎么做?我在想它可能是对数据库或XML的改进结构。

下面的当前XSL。

    <xsl:for-each select="iOverview/itinLeg">
      <xsl:sort select="flights/flDate" order="descending"/>
      <xsl:sort select="accommodation/accDate" order="descending"/>
      <xsl:apply-templates select="flights[count(.|key('fDate', flDate)[1])=1]"/>
      <xsl:apply-templates select="accommodation"/>
    </xsl:for-each>

    <xsl:template match="flights">
      <fo:block margin-top="2mm">
        <xsl:for-each select="key('fDate', flDate)">
          <xsl:sort select="flDate"/>
          <fo:block>
              <fo:inline font-weight="bold"><xsl:value-of select="flDate"/></fo:inline>
              <fo:inline padding-left="4mm"><xsl:value-of select="flFrom"/></fo:inline>
              <fo:inline padding-left="1mm">to</fo:inline>
              <fo:inline padding-left="2mm"><xsl:value-of select="flTo"/></fo:inline>
          </fo:block>
          .......
      </xsl:for-each>
    </fo:block>
  </xsl:template>

住宿xsl真的只是不同的元素名称。

感谢您提出任何建议。

3 个答案:

答案 0 :(得分:1)

首先,您在使用此日期格式排序时遇到问题。将其更改为2016-10-05之类的内容,然后您可以将日期排序为字符串。

您的下一个挑战是计算航班排序键的表达式与酒店的排序键不同,但您希望排序为单个序列。但这很容易克服;只需在logger:#ILogger

中使用联合表达式
xsl:sort/@select

答案 1 :(得分:0)

您的MCVE不是那么完整,所以我添加了<xsl:key name="fDate" match="flights" use="flDate" />来完成它。我还从XSLT中推断出了周围的标签。可以将搜索表达式合并到flDate | accDate来对输出进行排序。为清楚起见,我省略了XSL:FO块,您可能希望将它们读入最终解决方案。

生成的XSLT-1.0样式表如下所示:

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

  <xsl:key name="fDate" match="flights" use="flDate" />

  <xsl:template match="/">
    <xsl:for-each select="iOverview/itinLeg">
      <xsl:for-each select="flights | accommodation">
        <xsl:sort select="flDate | accDate" order="descending"/>
        <xsl:apply-templates select="."/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="flights[count(.|key('fDate', flDate)[1]) = 1]">
    <xsl:for-each select="key('fDate', flDate)">
      <xsl:value-of select="flDate"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(flFrom)"/> to <xsl:value-of select="flTo"/><xsl:value-of select="'&#10;'" />
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="flights[count(.|key('fDate', flDate)[1]) > 1]" />      <!-- filter out any dates with more than one matches -->

  <xsl:template match="accommodation">
    <xsl:value-of select="accDate"/><xsl:text> </xsl:text><xsl:value-of select="accName"/><xsl:value-of select="'&#10;'" />
  </xsl:template>    
</xsl:stylesheet>

根据输入数据,输出如下:

22OCT Philadelphia, PA - International to Glasgow International 
14OCT Hotel Los Jameos
13OCT Glasgow International to Philadelphia, PA - International 

如果您认为时间顺序错误,只需更换&#34;降序&#34;用&#34;升序&#34;在`&#34;。

答案 2 :(得分:0)

让我们将示例缩小到处理按给定DMMM格式按日期排序节点问题所需的最小值(至少看起来如何)。

给出以下输入:

<强> XML

<itinerary>
    <flight>
        <flDate>25OCT</flDate>
    </flight>
    <flight>
        <flDate>13NOV</flDate>
    </flight>
    <flight>
        <flDate>9SEP</flDate>
    </flight>
    <flight>
        <flDate>13OCT</flDate>
    </flight>
    <flight>
        <flDate>28AUG</flDate>
    </flight>
    <flight>
        <flDate>5OCT</flDate>
    </flight>
</itinerary>

以下样式表:

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:variable name="months" select="'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'" />

<xsl:template match="/itinerary">
    <xsl:copy>
        <xsl:for-each select="flight">
            <xsl:sort select="string-length(substring-before($months, translate(flDate, '0123456789', '')))" data-type="text" order="ascending"/>
            <xsl:sort select="translate(flDate, 'ABCDEFGJLMNOPRSTUVY', '')" data-type="number" order="ascending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

将返回:

<?xml version="1.0" encoding="UTF-8"?>
<itinerary>
  <flight>
        <flDate>28AUG</flDate>
    </flight>
  <flight>
        <flDate>9SEP</flDate>
    </flight>
  <flight>
        <flDate>5OCT</flDate>
    </flight>
  <flight>
        <flDate>13OCT</flDate>
    </flight>
  <flight>
        <flDate>25OCT</flDate>
    </flight>
  <flight>
        <flDate>13NOV</flDate>
    </flight>
</itinerary>

注意:当您的行程跨越一年边界时,这将产生不正确的结果:明年1月的事件将在今年12月的事件之前排序。