xsl:排序多个属性

时间:2016-07-05 05:52:55

标签: xslt

我有以下数据,其中我有多个属性: 我需要按日期时间或按时区按升序排序。 如果输入包含不同的日期时间和相同的时区,则应按每个日期时间输出升序。 如果输入包含相同的日期时间和不同的时区,则应按时区输出升序。

当前逻辑:

    <xsl:sort select="@Datetime" order="ascending"/>
    <xsl:sort select="@Timezone" order="ascending"/>

SAMPLE 1st scenario - 输出符合预期:

<MSG>
<DOC>
<Parent>
<Input Id="1234567890" Srvce="RRR" Cd="D1" Datetime="2016-06-16 20:42:30"    Timezone="+02:00" EvtRmk="DUMMY 1">
</Input>
<Input Id="1234567890" Srvce="RRR" Cd="D1" Datetime="2016-06-15 20:43:15" Timezone="+04:00" EvtRmk="DUMMY 1">
</Input>

当前输出:

    <Output>2016-06-15 20:43:15"</Output>
    <Output>2016-06-16 20:42:30</Output>

第二个场景示例 - 不同的时区/相同的日期时间;输出未按时区排序:

<MSG>
</DOC>
<Parent>
<Input Id="1234567890" Srvce="RRR" Cd="D1" Datetime="2016-06-15 20:42:30" Timezone="+04:00" EvtRmk="DUMMY 1">
</Input>
<Input Id="1234567890" Srvce="RRR" Cd="D1" Datetime="2016-06-15 20:43:15" Timezone="+00:00" EvtRmk="DUMMY 1">
</Input>

当前输出:

<Output>2016-06-15 20:42:30</Output>
<Output>2016-06-15 20:43:15</Output>

预期产出:

<Output>2016-06-15 20:43:15</Output>
<Output>2016-06-15 20:42:30</Output>

1 个答案:

答案 0 :(得分:0)

XSLT 1.0 中,您必须在两次传递中执行此操作:首先,将输入日期时间均衡为公共基数(如UTC),然后按此值对结果进行排序:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="Parent">
    <!-- first-pass -->
    <xsl:variable name="inputs">
        <xsl:for-each select="Input">
            <input>
                <xsl:copy-of select="@*"/>
                <xsl:call-template name="dateTime-to-seconds">
                    <xsl:with-param name="dateTime" select="@Datetime" />
                    <xsl:with-param name="offset" select="@Timezone" />
                </xsl:call-template>
            </input>    
        </xsl:for-each>
    </xsl:variable>
    <!-- output -->
    <xsl:copy>
        <xsl:for-each select="exsl:node-set($inputs)/input">
            <xsl:sort select="." data-type="number" order="ascending"/>
            <Input>
                <xsl:copy-of select="@*"/>
            </Input>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

<xsl:template name="dateTime-to-seconds">
    <xsl:param name="dateTime"/>
    <xsl:param name="offset"/>

    <xsl:variable name="date" select="substring-before($dateTime, ' ')" />
    <xsl:variable name="local-time" select="substring-after($dateTime, ' ')" />

    <xsl:variable name="year" select="substring($date, 1, 4)" />
    <xsl:variable name="month" select="substring($date, 6, 2)" />
    <xsl:variable name="day" select="substring($date, 9, 2)" />

    <xsl:variable name="hour" select="substring($local-time, 1, 2)" />
    <xsl:variable name="minute" select="substring($local-time, 4, 2)" />
    <xsl:variable name="second" select="substring($local-time, 7)" />

    <xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
    <xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
    <xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />

    <xsl:variable name="a" select="floor((14 - $month) div 12)"/>
    <xsl:variable name="y" select="$year + 4800 - $a"/>
    <xsl:variable name="m" select="$month + 12*$a - 3"/>    
    <xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />

    <xsl:value-of select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
</xsl:template>

</xsl:stylesheet> 

应用于以下测试输入:

<强> XML

<Parent>
    <Input Id="001" Datetime="2016-06-15 19:44:30" Timezone="-02:30"/>
    <Input Id="002" Datetime="2016-06-15 20:42:30" Timezone="+04:00"/>
    <Input Id="003" Datetime="2016-06-15 21:43:15" Timezone="+00:00"/>
</Parent>

结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<Parent>
   <Input Id="002" Datetime="2016-06-15 20:42:30" Timezone="+04:00"/>
   <Input Id="003" Datetime="2016-06-15 21:43:15" Timezone="+00:00"/>
   <Input Id="001" Datetime="2016-06-15 19:44:30" Timezone="-02:30"/>
</Parent>