XSLT 1.0: Sorting by concating portions of date string

前端 未结 2 1405
轻奢々
轻奢々 2021-01-26 00:20

I\'m trying to take XML data and sort elements by their data attribute. Unfortunately the dates come over in mm/dd/yyyy format and are not static lengths. (Jan = 1 instead of

相关标签:
2条回答
  • 2021-01-26 00:33

    Here is one way to do this sorting using a 2-pass transformation (it is possible to do this in a one-pass transformation, but the code would be too-complicated):

    <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>
    

    when this transformation is applied on the provided XML document:

    <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>
    

    the wanted, correct result is produced:

    <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>
    

    Do note:

    1. Multipass transformations in XSLT 1.0 require the use of the vendor-specific xxx:node-set() function to convert the result of a pass from its RTF (Result Transformation Fragment) type to a regular tree (document).

    2. The xxx:node-set() function used in this solution is the ext:node-set() function of EXSLT, which is implemented on most XSLT processors.

    0 讨论(0)
  • 2021-01-26 00:51

    You can use multiple xsl:sort instructions like in this stylesheet:

    <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>
    

    Output:

    <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>
    

    Update from comments

    What I'm trying to do is in that stylesheet perform the sort as you've done and then export out the promotionid of the item with startdate '2/19/2011' in this case. I assumed it would be something like <xsl:value-of select="data[last()]/@promotionid"/> but I either am using it in the wrong place or have the statement wrong

    Update 3: Now with new selecting data conditions

    Use the "standard" maximum idiom. This stylesheet:

    <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>
    

    Output:

    64636
    
    0 讨论(0)
提交回复
热议问题