如何使用XSLT对RSS订阅进行分组

3

我有一个从互联网上读取的包含数据的XML文件。该XML文件是标准的RSS 2.0文件,格式如下(为了缩短文章,我省略了一些标签):

<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title/>
    <item>
      <title>Blah</title>
      <category>CAT1</category>
    </item>
    <item>
      <title>Blah2</title>
      <category>CAT2</category>
    </item>
    <item>
      <title>Blah3</title>
      <category>CAT1</category>
    </item>
  </channel>
</rss>

我将使用XSLT创建一个HTML文件。我的问题是我需要按CATEGORY标签对项目进行分组,以生成类似以下内容的东西:
<div>
  <span>CAT1</span>
  <div>
    <span>Blah</span>
    <span>Blah3</span>
  </div>
</div>
<div>
  <span>CAT2</span>
  <div>
    <span>Blah2</span>
  </div>
</div>

到目前为止,我发现有许多帖子教授如何使用XSLT按属性进行分组(例如这个这个这个)。但是,我尝试适应它们的所有尝试都失败了。
谢谢您的帮助,
鲍勃
4个回答

2
使用Muenchian方法进行分组,这很容易解决。以下是样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="byCategory" match="item" use="category" />
    <xsl:template match="/">
        <html><xsl:apply-templates /></html>
    </xsl:template>
    <xsl:template
        match="item[generate-id()=generate-id(key('byCategory', category)[1])]">
        <div>
            <span><xsl:apply-templates select="category" /></span>
            <xsl:apply-templates select="key('byCategory', category)" 
                mode="out" />
        </div>
    </xsl:template>
    <xsl:template match="item"/>
    <xsl:template match="item" mode="out">
        <div><xsl:apply-templates select="title" /></div>
    </xsl:template>
</xsl:stylesheet>

应用于此输入:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title />
        <item>
            <title>Blah</title>
            <category>CAT1</category>
        </item>
        <item>
            <title>Blah2</title>
            <category>CAT2</category>
        </item>
        <item>
            <title>Blah3</title>
            <category>CAT1</category>
        </item>
    </channel>
</rss>

生成:

<html>
    <div>
        <span>CAT1</span>
        <div>Blah</div>
        <div>Blah3</div>
    </div>
    <div>
        <span>CAT2</span>
        <div>Blah2</div>
    </div>
</html>

1

这应该能让你开始了

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

>
    <xsl:output method="html" indent="yes" omit-xml-declaration="yes"/>

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

    <xsl:template match="item[not(category = preceding-sibling::item/category)]">
        <xsl:variable name="category" select="category"/>
        <div>
            <span>
                <xsl:value-of select="category"/>
            </span>
            <div>
            <span>
                <xsl:value-of select="title"/>
            </span>
            <xsl:apply-templates select="following-sibling::item[category=$category]" mode="extra"/>
            </div>
        </div>
    </xsl:template>

    <xsl:template match="item" mode="extra">
        <span>
            <xsl:value-of select="title"/>
        </span>
    </xsl:template>
</xsl:stylesheet>

1
如果您能够使用XSLT 2.0,那么您应该能够使用for-each-group来对所有内容进行分组。
例如,使用您的示例XML输入,可以使用以下XSLT 2.0样式表:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="rss">
    <html>
      <xsl:apply-templates/>
    </html>
  </xsl:template>

  <xsl:template match="channel">
      <xsl:for-each-group select="item" group-by="category">
        <xsl:variable name="catType" select="category"/>
        <div>
          <span>
            <xsl:value-of select="$catType"/>
          </span>
          <div>
            <xsl:apply-templates select="../item[category=$catType]/title"/>
          </div>
        </div>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="title">
    <span>
      <xsl:apply-templates/>
    </span>
  </xsl:template>

  <xsl:template match="category"/>

  <xsl:template match="item">
    <xsl:apply-templates/>
  </xsl:template>

</xsl:stylesheet>

会产生以下输出:

<html>
   <div>
      <span>CAT1</span>
      <div>
         <span>Blah</span>
         <span>Blah3</span>
      </div>
   </div>
   <div>
      <span>CAT2</span>
      <div>
         <span>Blah2</span>
      </div>
   </div>
</html>

2.0的解决方案更加优雅。但是,由于我们正在使用1.0,我会继续使用它。谢谢。 - Bob Rivers

1

XSLT 2.0 使用 xsl:for-each-group 功能:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*/*">
        <xsl:for-each-group select="item" group-by="category">
            <div>
                <span>
                    <xsl:value-of select="category"/>
                </span>
                <div>
                    <xsl:apply-templates select="current-group()/title"/>
                </div>
            </div>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="title">
        <span>
            <xsl:value-of select="."/>
        </span>
    </xsl:template>

</xsl:stylesheet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接