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

发布于 2024-10-11 18:27:42 字数 3967 浏览 2 评论 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

我真的希望我能更好地理解这个哈哈无论如何,我使用了您提供的代码并添加了在您在另一个问题中提供的相关代码中工作的“值”代码,并且我没有看到任何结果。理想情况下,一旦排序完毕,我就需要引用最新数据元素中的多个其他属性。

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

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

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

更新 3

好吧,显然我们已经多次收到错误的信息。我已经使用另一个属性更新了示例 XML,该属性更改了需要完成的操作。

需要做的是像我们已经完成的那样按日期对数据进行排序,然后提取最新且类型为“base”的数据节点的promotionid。如果没有数据节点具有 type='base' ,那么我们只需引用最新的数据节点,就像我们已经在工作的那样。

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

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 01) So I believe the string will have to be parsed into three components and the month padded. The newly concated value (yyyymmdd) then sorted descending.

Problem is I have no idea how to do this. Here is an example of the data

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

Also can anyone please recommend a good book on learning XSLT?

Thanks!

Update 1

I really wish I had a better understanding of this LOL Anyways, I used the code you provided and added the 'value-of' code that worked in the related code you provide in another question and am seeing no results. Ideally once this has been sorted I would then need to reference multiple other attributes from the most recent data element.

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

Update 2

Hmm, well I tried updating it like you said

...
</xsl:template>

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

And I still don't get any output. I googled around a bit and also tried messing with this declaration xmlns:ext="http://exslt.org/common" and tried different values based on an article I looked at. I tried

And nothing provided output. So I wonder if I have something wrong or if my xslt processor doesn't support it.

Update 3

Okay apparently we've been given, repeatedly, bad information. I've update the sample XML with another attribute which changes what needs to be done.

What needs to happen is the data be sorted by date like we've already done and then pull the promotionid of the data node that is the most recent AND has the type='base'. If no data node has type='base' than we just reference the most recent data node like we've already have working.

Hope that makes sense. And once again thanks so much.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

迷离° 2024-10-18 18:27:42

您可以使用多个 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>

从注释更新

我想做的是
样式表按照您的方式执行排序
完成然后导出
开始日期的商品的促销 ID
在本例中为“2/19/2011”。我以为
会是这样的

但我要么用错了它
表述有误或有错误

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

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
¢好甜 2024-10-18 18:27:42

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

<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 处理器上实现。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文