XSLT 1.0:通过连接日期字符串的部分进行排序

时间:2011-01-14 17:50:52

标签: xml datetime xslt sorting xslt-1.0

我正在尝试通过数据属性获取XML数据和排序元素。不幸的是,日期以mm / dd / yyyy格式出现,并不是静态长度。 (Jan = 1而不是01)所以我认为字符串必须被解析为三个组件并填充月份。新结合的值(yyyymmdd)然后按降序排序。

问题是我不知道该怎么做。以下是数据的示例

<content date="1/13/2011 1:21:00 PM">
    <collection vo="promotion">
        <data vo="promotion" promotionid="64526" code="101P031" startdate="1/7/2011 12:00:00 AM" type="base"/>
        <data vo="promotion" promotionid="64646" code="101P026" startdate="2/19/2011 12:00:00 AM" type=""/>
        <data vo="promotion" promotionid="64636" code="101P046" startdate="1/9/2011 12:00:00 AM" type="base"/>
    </collection>
</content>

也有人可以推荐一本关于学习XSLT的好书吗?

谢谢!

更新1

我真的希望我能更好地理解这个LOL无论如何,我使用了你提供的代码,并添加了在你提供的相关代码中工作的'value-of'代码,并且看不到任何结果。理想情况下,一旦对其进行了排序,我就需要从最新的数据元素中引用多个其他属性。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

    <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="/">
      <xsl:variable name="vrtfPass1">
        <xsl:apply-templates/>
      </xsl:variable>
      <xsl:apply-templates mode="pass2" select=
       "ext:node-set($vrtfPass1)/*"/>
    </xsl:template>

    <xsl:template match="@startdate">
     <xsl:variable name="vDate" select="substring-before(.,' ')"/>

     <xsl:variable name="vYear" select=
       "substring($vDate, string-length($vDate) -3)"/>
     <xsl:variable name="vDayMonth" select=
      "substring-before($vDate, concat('/',$vYear))"/>

      <xsl:variable name="vMonth"
        select="format-number(substring-before($vDayMonth, '/'), '00')"/>
      <xsl:variable name="vDay"
        select="format-number(substring-after($vDayMonth, '/'), '00')"/>

    <xsl:attribute name="startdate">
      <xsl:value-of select="concat($vYear,$vMonth,$vDay)"/>
    </xsl:attribute>
    </xsl:template>

    <xsl:template match="node()|@*" mode="pass2">
      <xsl:copy>
         <xsl:apply-templates mode="pass2" select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template mode="pass2" match="collection">
      <xsl:copy>
       <xsl:apply-templates mode="pass2" select="@*"/>
       <xsl:apply-templates mode="pass2">
        <xsl:sort select="@startdate"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="content/collection/data">
        <xsl:if test="position()=1">
                   <xsl:value-of select="@promotionid"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

更新2

嗯,好吧,我试着像你说的那样更新它

...
</xsl:template>

    <xsl:template mode="pass2" match="content/collection/data">
                   <xsl:value-of select="@promotionid"/>
    </xsl:template>
</xsl:stylesheet>

我仍然没有得到任何输出。我google了一下,并尝试搞乱这个声明xmlns:ext="http://exslt.org/common"并根据我看过的文章尝试了不同的值。我试过了

没有提供输出。所以我想知道我是否有问题,或者我的xslt处理器是否支持它。

更新3

好吧,显然我们已经多次获得了不良信息。我用另一个属性更新了示例XML,这个属性改变了需要做的事情。

需要发生的是按照我们已经完成的日期对数据进行排序,然后拉出最新的数据节点的promotionid并且type ='base'。如果没有数据节点具有type ='base',那么我们只是引用最新的数据节点,就像我们已经有效一样。

希望这是有道理的。再一次非常感谢。

2 个答案:

答案 0 :(得分:1)

您可以使用此样式表中的多个xsl:sort说明:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="collection">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="data">
            <xsl:sort select="substring-after(
                                 substring-after(
                                    substring-before(
                                       @startdate,
                                       ' '),
                                    '/'),
                                 '/')" data-type="number"/>
            <xsl:sort select="substring-before(
                                 @startdate,
                                 '/')" data-type="number"/>
            <xsl:sort select="substring-before(
                                 substring-after(
                                    @startdate,
                                    '/'),
                                 '/')" data-type="number"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

输出:

<content date="1/13/2011 1:21:00 PM">
    <collection vo="promotion">
        <data vo="promotion" promotionid="64526" code="101P031"
              startdate="1/7/2011 12:00:00 AM"></data>
        <data vo="promotion" promotionid="64636" code="101P046"
              startdate="1/9/2011 12:00:00 AM"></data>
        <data vo="promotion" promotionid="64646" code="101P026"
              startdate="2/19/2011 12:00:00 AM"></data>
    </collection>
</content>

从评论中更新

  

我正在努力做的就是这样   样式表按您的方式执行排序   完成后再导出   使用startdate升级项目   在这种情况下,“2/19/2011”。我以为   它会是这样的   <xsl:value-of select="data[last()]/@promotionid"/>   但我要么在错误中使用它   放置或声明错误

更新3 :现在有了新的选择数据条件

使用“标准”最大成语。这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="collection">
        <xsl:variable name="vData" select="data[@type='base']"/>
        <xsl:for-each select="data[not($vData)]|$vData">
            <xsl:sort select="substring-after(
                                     substring-after(
                                        substring-before(
                                           @startdate,
                                           ' '),
                                        '/'),
                                     '/')" data-type="number"/>
            <xsl:sort select="substring-before(
                                     @startdate,
                                     '/')" data-type="number"/>
            <xsl:sort select="substring-before(
                                     substring-after(
                                        @startdate,
                                        '/'),
                                     '/')" data-type="number"/>
            <xsl:if test="position()=last()">
                <xsl:value-of select="@promotionid"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

输出:

64636

答案 1 :(得分:0)

以下是使用2遍转换进行排序的一种方法(可以在一次转换中执行此操作,但代码太复杂了):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

    <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="/">
      <xsl:variable name="vrtfPass1">
        <xsl:apply-templates/>
      </xsl:variable>
      <xsl:apply-templates mode="pass2" select=
       "ext:node-set($vrtfPass1)/*"/>
    </xsl:template>

    <xsl:template match="@startdate">
     <xsl:variable name="vDate" select="substring-before(.,' ')"/>

     <xsl:variable name="vYear" select=
       "substring($vDate, string-length($vDate) -3)"/>
     <xsl:variable name="vDayMonth" select=
      "substring-before($vDate, concat('/',$vYear))"/>

      <xsl:variable name="vMonth"
        select="format-number(substring-before($vDayMonth, '/'), '00')"/>
      <xsl:variable name="vDay"
        select="format-number(substring-after($vDayMonth, '/'), '00')"/>

    <xsl:attribute name="startdate">
      <xsl:value-of select="concat($vYear,$vMonth,$vDay)"/>
    </xsl:attribute>
    </xsl:template>

    <xsl:template match="node()|@*" mode="pass2">
      <xsl:copy>
         <xsl:apply-templates mode="pass2" select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template mode="pass2" match="collection">
      <xsl:copy>
       <xsl:apply-templates mode="pass2" select="@*"/>
       <xsl:apply-templates mode="pass2">
        <xsl:sort select="@startdate"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档

<content date="1/13/2011 1:21:00 PM">
    <collection vo="promotion">
        <data vo="promotion" promotionid="64526" code="101P031" startdate="1/7/2011 12:00:00 AM"/>
        <data vo="promotion" promotionid="64646" code="101P026" startdate="2/19/2011 12:00:00 AM"/>
        <data vo="promotion" promotionid="64636" code="101P046" startdate="1/9/2011 12:00:00 AM"/>
    </collection>
</content>

产生了想要的正确结果

<content date="1/13/2011 1:21:00 PM">
   <collection vo="promotion">
      <data vo="promotion" promotionid="64526" code="101P031" startdate="20110107"/>
      <data vo="promotion" promotionid="64636" code="101P046" startdate="20110109"/>
      <data vo="promotion" promotionid="64646" code="101P026" startdate="20110219"/>
   </collection>
</content>

请注意

  1. XSLT 1.0中的多通道转换需要使用特定于供应商的xxx:node-set()函数将传递结果从其RTF(结果转换片段)类型转换为常规树(文件)。

  2. 此解决方案中使用的xxx:node-set()函数是EXSLT的ext:node-set()函数,它在大多数XSLT处理器上实现。