使用XSLT合并时间段

时间:2014-06-10 08:11:33

标签: xslt xslt-2.0

我正在尝试通过使用XSLT合并和计算它们来转换时间段,我不知道该怎么做。 例: 我有以下时期:

<root>
        <period begin="2014-06-01" end="2014-06-15" />
        <period begin="2014-06-07" end="2014-06-10" />
        <period begin="2014-06-08" end="2014-06-12" />
</root>

这些应在以下时段转换:

<root>
        <mergedperiod begin="2014-06-01" end="2014-06-07" amount="1"/>
        <mergedperiod begin="2014-06-07" end="2014-06-08" amount="2"/>
        <mergedperiod begin="2014-06-08" end="2014-06-10" amount="3"/>
        <mergedperiod begin="2014-06-10" end="2014-06-12" amount="2"/>
        <mergedperiod begin="2014-06-12" end="2014-06-15" amount="1"/>
</root>

有关如何处理此事的任何建议?

2 个答案:

答案 0 :(得分:0)

这个怎么样:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                exclude-result-prefixes="xs">

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/root">
    <!-- generate a sequence of all distinct dates, in chronological order -->
    <xsl:variable name="dates" as="xs:date*">
      <xsl:perform-sort>
        <xsl:sort />
        <xsl:sequence select="distinct-values(
              (period/xs:date(@begin), period/xs:date(@end)))" />
      </xsl:perform-sort>
    </xsl:variable>
    <!-- store the set of input <period> elements for future use -->
    <xsl:variable name="allPeriods" select="period" />

    <root>
      <xsl:for-each select="1 to (count($dates) - 1)">
        <xsl:variable name="i" select="." />
        <!-- how many periods include the current bracket between this date
             and the next one? -->
        <xsl:variable name="numPeriods" select="count($allPeriods[
           xs:date(@begin) le $dates[$i] and xs:date(@end) ge $dates[$i+1]])" />
        <!-- if there is at least one, output a new <mergedperiod> -->
        <xsl:if test="$numPeriods gt 0">
          <mergedperiod begin="{$dates[$i]}" end="{$dates[$i+1]}"
                        amount="{$numPeriods}" />
        </xsl:if>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

此样式表可避免输出mergedperiod amount为零(即如果您的输入具有非连续句点)。如果你确实想要包含零,那么只需删除if

编辑:从评论中,如果要合并共享相同计数的连续句点,那么最简单的方法是将合并句点的初始列表生成为变量,然后发布进行合并的过程:

    <xsl:variable name="pass1">
      <xsl:for-each select="1 to (count($dates) - 1)">
        <xsl:variable name="i" select="." />
        <!-- how many periods include the current bracket between this date
             and the next one? -->
        <xsl:variable name="numPeriods" select="count($allPeriods[
           xs:date(@begin) le $dates[$i] and xs:date(@end) ge $dates[$i+1]])" />
        <!-- if there is at least one, output a new <mergedperiod> -->
        <xsl:if test="$numPeriods gt 0">
          <mergedperiod begin="{$dates[$i]}" end="{$dates[$i+1]}"
                        amount="{$numPeriods}" />
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>
    <root>
      <!-- adjacent periods with the same amount -->
      <xsl:for-each-group select="$pass1/*" group-adjacent="@amount">
        <!-- where the end of one lines up with the start if the next -->
        <xsl:for-each-group select="current- group()" group-starting-with="*[not(@begin = preceding-sibling::*[1]/@end)]">
          <mergedperiod begin="{@begin}"
              end="{current-group()[last()]/@end}"
              amount="{@amount}"/>
        </xsl:for-each-group>
      </xsl:for-each-group>
    </root>

答案 1 :(得分:0)

编辑添加了分组,因此可以避免重复。

这是一个可能的答案,如果有一个下一个更大的日期,这基本上会为beginn或end属性生成一个mergedperiod:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

<xsl:output indent="yes"/>

<xsl:template match="root">
    <root>
        <xsl:variable name="periods">
            <xsl:apply-templates select="period/@begin|period/@end"/>
        </xsl:variable>
        <xsl:for-each-group select="$periods/*" group-by="@begin">
            <xsl:sort select="@begin"/>
            <xsl:copy-of select="."/>
        </xsl:for-each-group>
    </root>
</xsl:template>

<xsl:template match="period/@begin|period/@end">
    <xsl:variable name="date" select="xs:date(.)"/>
    <xsl:variable name="nextdate"
        select="min(for $i in //period/(@end|@begin)[xs:date(.) gt $date] return xs:date($i))"/>
    <xsl:if test="string($nextdate) != ''">
        <mergedperiod begin="{$date}" end="{$nextdate}"
            amount="{count(//period[xs:date(@begin) le $date and xs:date(@end) ge $nextdate])}"/>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>