0

I realize many similar questions have been asked, but I have read about a dozen great examples and still been unable to combine them all into a working solution.

I have an RSS feed with the following structure:

<root>
    <pubDate>Tue, 03 Sep 2013 15:15:00 +0000</pubDate>
    <title>Title 1</title>
    <pubDate>Tue, 02 Mar 2013 15:15:00 +0000</pubDate>
    <title>Title 2</title>
    <pubDate>Tue, 02 Sep 2012 15:15:00 +0000</pubDate>
    <title>Title 3</title>
    ...
</root>

It is flattened out because the feed has some very large nodes that slow the page down enormously, if it loads at all. Therefore, when I pull it in, I limit the data chosen to just the title and pubDate fields, which removes the hierarchy. (Other suggestions here maybe?)

I want to display the data grouped by year:

<year handle="2013">
    <date>Tue, 03 Sep 2013 15:15:00 +0000</date>
    <title>Title 1</title>
    <date>Tue, 02 Mar 2013 15:15:00 +0000</date>
    <title>Title 2</title>
</year>
<year handle="2012">
    <date>Tue, 02 Sep 2012 15:15:00 +0000</date>
    <title>Title 3</title>
    ...
</year>
...

I can parse out the year with substring-before(substring-after(substring-after(substring-after(pubDate, ' '), ' '), ' '), ' '), and I have attempted to create a key with:

<xsl:key name="years" match="/root/pubDate" use="substring-before(substring-after(substring-after(substring-after(/root/pubDate, ' '), ' '), ' '), ' ')" />

Which I then used:

<xsl:template match="root">
<rss>
    <xsl:apply-templates mode="year" select="pubDate[
      generate-id()
      =
      generate-id(key('years', substring-before(substring-after(substring-after(substring-after(/root/pubDate, ' '), ' '), ' '), ' '))[1])
    ]"/>
</rss>
</xsl:template>

<xsl:template match="root/pubDate" mode="year">
    <xsl:variable name="year" select="substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' ')"/>
    <year handle="{$year}">
         <xsl:apply-templates mode="final" select="key('years', $year)"/>
    </year>
</xsl:template>

<xsl:template match="root/pubDate" mode="final">
    <date>
        <xsl:value-of select="." />
    </date> 
    <title><xsl:value-of select="./following-sibling::*[1]" /></title>
</xsl:template>

But my output is:

<year handle="2013">
    <date>Tue, 03 Sep 2013 15:15:00 +0000</date>
    <title>Title 1</title>
    <date>Tue, 02 Mar 2013 15:15:00 +0000</date>
    <title>Title 2</title>
    <date>Tue, 02 Sep 2012 15:15:00 +0000</date>
    <title>Title 3</title>
    ...
</year>

I seem to be able to solve individual parts of this problem but I have not been able to get them all working together. There are many examples of grouping RSS by year and month, but that is just different enough from my problem (especially considering my flattened RSS) that I cannot emulate those examples.

If it helps to know, I am using XSLT / XPath 1.0 because I am using Symphony CMS, which relies on PHP's libxslt.

Any help is very welcome, thanks for reading.

4

2 回答 2

1

XSLT 的一种极限情况,在这种情况下,您可以使用一些真实的代码来提高效率。

我会尝试的第一件事是限制源返回的提要,也许您的 RSS 查询提供程序支持任何限制内容的参数(按日期、ID)。

否则,如果加载完整的 RSS 很慢,我认为先转换数据不会更快。

如果您关心页面加载时间,您可以使用异步 AJAX 延迟加载 RSS。

于 2013-10-16T20:18:18.540 回答
0

您的代码中有两个 XPath 错误,使用/root/pubDate而不是(正确的)当前节点。这个固定的样式表提供了所需的输出:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="years" match="/root/pubDate" use="substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' ')" />

  <xsl:template match="root">
    <rss>
      <xsl:apply-templates mode="year" select="pubDate[
        generate-id()
        =
        generate-id(key('years', substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' '))[1])
        ]"/>
    </rss>
  </xsl:template>

  <xsl:template match="root/pubDate" mode="year">
    <xsl:variable name="year" select="substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' ')"/>
    <year handle="{$year}">
      <xsl:apply-templates mode="final" select="key('years', $year)"/>
    </year>
  </xsl:template>

  <xsl:template match="root/pubDate" mode="final">
    <date>
      <xsl:value-of select="." />
    </date>
    <title><xsl:value-of select="./following-sibling::*[1]" /></title>
  </xsl:template>

</xsl:stylesheet>

还有更短的解决方案。恕我直言,在这种情况下使用不同的模板并没有成功,因为它不会使代码更具可读性(并且不需要模板的可重用性)。此外,我会以不同的方式解决细节。您可能会考虑以下代码,或者可能是其中的一部分:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="pubdates-by-year" match="/root/pubDate" use="substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' ')" />

  <xsl:template match="root">
    <rss>
      <xsl:for-each select="pubDate[count(. | key('pubdates-by-year', substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' '))[1]) = 1]">
        <xsl:variable name="year" select="substring-before(substring-after(substring-after(substring-after(., ' '), ' '), ' '), ' ')"/>
        <year handle="{$year}">
          <xsl:for-each select="key('pubdates-by-year', $year)">
            <xsl:copy-of select="."/>
            <xsl:copy-of select="following-sibling::title[1]"/>
          </xsl:for-each>
        </year>
      </xsl:for-each>
    </rss>
  </xsl:template>

</xsl:stylesheet>
于 2016-12-03T20:11:43.830 回答