在同一时间内组节点

时间:2017-09-30 08:11:05

标签: xslt

这是我的实际数据(gps跟踪点)的简化版本。 我有一个按升序排列的时间列表。

<gpx>
    <trk>
        <trkseg>
            <trkpt>
                <time>2000-01-01T15:25:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:26:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:27:00Z</time>
            </trkpt>
        </trkseg>
    </trk>
    <trk>
        <trkseg>
            <trkpt>
                <time>2000-01-01T15:28:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:29:00Z</time>
            </trkpt>            
        </trkseg>
        <trkseg>
            <trkpt>
                <time>2000-01-01T16:00:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T16:01:00Z</time>
            </trkpt>            
        </trkseg>
    </trk>
</gpx>

我想检测&#34;停止&#34;并将连续的时间分组。 例如,使用&#34;停止&#34;如果没有5分钟或更长时间的数据,我会得到

<gpx >
    <trk>
        <trkseg>
            <trkpt>
                <time>2000-01-01T15:25:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:26:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:27:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:28:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T15:29:00Z</time>
            </trkpt>    
        </trkseg>
    </trk>
    <trk>
        <trkseg>
            <trkpt>
                <time>2000-01-01T16:00:00Z</time>
            </trkpt>
            <trkpt>
                <time>2000-01-01T16:01:00Z</time>
            </trkpt>            
        </trkseg>
    </trk>
</gpx>

1 个答案:

答案 0 :(得分:1)

这是XQuery 3.0 / 3.1中tumbling window子句的一个很好的用例:

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:indent "yes";

<gpx>
{
let $dateTimes := //time/xs:dateTime(.),
    $stopDuration := xs:dayTimeDuration('PT5M')
for tumbling window $w in $dateTimes
start when true()
end $e next $n when $n - $e gt $stopDuration
return
  <trk>
    <trkseg>
    {
      $w!<trkpt><time>{.}</time></trkpt>
    }    
    </trkseg>
  </trk>
}
</gpx>

结果是

<?xml version="1.0" encoding="UTF-8"?>
<gpx>
   <trk>
      <trkseg>
         <trkpt>
            <time>2000-01-01T15:25:00Z</time>
         </trkpt>
         <trkpt>
            <time>2000-01-01T15:26:00Z</time>
         </trkpt>
         <trkpt>
            <time>2000-01-01T15:27:00Z</time>
         </trkpt>
         <trkpt>
            <time>2000-01-01T15:28:00Z</time>
         </trkpt>
         <trkpt>
            <time>2000-01-01T15:29:00Z</time>
         </trkpt>
      </trkseg>
   </trk>
   <trk>
      <trkseg>
         <trkpt>
            <time>2000-01-01T16:00:00Z</time>
         </trkpt>
         <trkpt>
            <time>2000-01-01T16:01:00Z</time>
         </trkpt>
      </trkseg>
   </trk>
</gpx>

因此,如果您的XSLT处理器是Saxon 9,它也支持XQuery,您可以考虑使用XQuery而不是XSLT。

如果你想在XSLT中使用它,那么这里是一个XSLT 3.0样式表,可以与Saxon 9.8(任何版本)或Altova(2017版本)一起运行,试图使用xsl:iterate来模拟XQuery方法上面显示的翻滚窗口计算:

<?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"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs math mf"
    expand-text="yes"
    version="3.0">

    <xsl:variable name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>

    <xsl:output indent="yes"/>

    <xsl:function name="mf:wrap" as="element(trk)">
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <trk>
            <trkseq>
                <xsl:for-each select="$dateTimes">
                    <trkpt>
                        <time>
                            <xsl:value-of select="."/>
                        </time>
                    </trkpt>
                </xsl:for-each>
            </trkseq>
        </trk>
    </xsl:function>

    <xsl:template match="gpx">
        <xsl:copy>
            <xsl:variable name="dateTimes" as="xs:dateTime*" select=".//time/xs:dateTime(.)"/>
            <xsl:iterate select="$dateTimes">
                <xsl:param name="w" as="xs:dateTime*" select="()"/>
                <xsl:param name="e" as="xs:dateTime?" select="()"/>
                <xsl:on-completion>
                    <xsl:sequence select="mf:wrap($w)"/>
                </xsl:on-completion>
                <xsl:variable name="n" as="xs:dateTime" select="."/>
                <xsl:choose>
                    <xsl:when test="exists($e) and exists($n) and $n - $e gt $stop">
                        <xsl:sequence select="mf:wrap($w)"/>
                        <xsl:next-iteration>
                            <xsl:with-param name="w" select="$n"/>
                            <xsl:with-param name="e" select="$n"/>
                        </xsl:next-iteration>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:next-iteration>
                            <xsl:with-param name="w" select="$w, $n"/>
                            <xsl:with-param name="e" select="$n"/>
                        </xsl:next-iteration>
                    </xsl:otherwise>
                </xsl:choose>            
            </xsl:iterate>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

如果您需要在XSLT 2.0中执行此操作,一种方法是编写一个逐项递归处理dateTime序列的函数,并在找到与日期相比具有更大差异的日期时,分别返回一个组trk元素。允许限制(所以基本上实现了XQuery检查end $e next $n when $n - $e gt $stopDuration):

<?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" xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf" version="2.0">

    <xsl:param name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>

    <xsl:output indent="yes"/>

    <xsl:function name="mf:group" as="element(trk)*">
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <xsl:param name="stop" as="xs:dayTimeDuration"/>
        <xsl:sequence select="mf:group($dateTimes[1], $dateTimes[position() gt 1], $stop)"/>
    </xsl:function>

    <xsl:function name="mf:group" as="element(trk)*">
        <xsl:param name="group" as="xs:dateTime*"/>
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <xsl:param name="stop" as="xs:dayTimeDuration"/>
        <xsl:variable name="next" as="xs:dateTime?" select="$dateTimes[1]"/>
        <xsl:variable name="end" as="xs:dateTime" select="$group[last()]"/>
        <xsl:choose>
            <xsl:when test="not(exists($next))">
                <xsl:sequence select="mf:wrap($group)"/>
            </xsl:when>
            <xsl:when test="$next - $end gt $stop">
                <xsl:sequence select="mf:wrap($group)"/>
                <xsl:sequence select="mf:group($next, $dateTimes[position() gt 1], $stop)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="mf:group(($group, $next), $dateTimes[position() gt 1], $stop)"
                />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>

    <xsl:function name="mf:wrap" as="element(trk)">
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <trk>
            <trkseq>
                <xsl:for-each select="$dateTimes">
                    <trkpt>
                        <time>
                            <xsl:value-of select="."/>
                        </time>
                    </trkpt>
                </xsl:for-each>
            </trkseq>
        </trk>
    </xsl:function>

    <xsl:template match="gpx">
        <xsl:copy>
            <xsl:sequence select="mf:group(.//time/xs:dateTime(.), $stop)"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>