我正在尝试通过使用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>
有关如何处理此事的任何建议?
答案 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>