XSLT在多个级别上排序

时间:2013-12-20 21:23:37

标签: xml sorting xslt

大家晚上好,

我创建了一个小程序,可以对目录中包含的所有音频文件(以及所有子目录)进行排序。我正在收集C#中的所有信息,以便生成以下XML文件(下面的XML是一个精简版本,我删除了这里不需要的所有属性):

<Directory Name="Compilations" >
  <Directory Name="Compil - 2010" >
    <File MediaTitle="4 Min" MediaAlbum="AA" MediaYear="2010" MediaArtists="Madonna" />
    <File MediaTitle="Beggin" MediaAlbum="AA" MediaYear="2010" MediaArtists="Madcon" />
  </Directory>
</Directory>

我想要以下结果:

<MediaYear Year="2010">
  <MediaArtists Artist="Madonna">
    <MediaAlbum Album="AA">
      <File MediaTitle="4 Min" MediaAlbum="AA" MediaYear="2010" MediaArtists="Madonna" />
    </MediaAlbum>
  </MediaArtists>
  <MediaArtists Artist="Madcon">
    <MediaAlbum Album="AA">
      <File MediaTitle="Beggin" MediaAlbum="AA" MediaYear="2010" MediaArtists="Madcon" />
    </MediaAlbum>
  </MediaArtists>
</MediaYear>

所有“文件”事件应按以下方式排序:

  • 艺术家
  • 相册

我使用的XSLT代码如下:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">

  <xsl:output method="xml" indent="yes"/>

  <xsl:strip-space elements="*" />

  <xsl:key match="File[@MediaYear != 0]" name="MediaYears" use="@MediaYear"/>

  <xsl:template match="/">

    <xsl:for-each select="//File[generate-id(.)= generate-id(key('MediaYears', @MediaYear)[1])]">

      <xsl:sort select="@MediaYear"/>

      <MediaYear>

        <xsl:attribute name="Year">

          <xsl:value-of select="@MediaYear"/>

        </xsl:attribute>

        <xsl:for-each select="key('MediaYears', @MediaYear)">

          <xsl:copy>

            <xsl:copy-of select="node() | @* | node()"/>

          </xsl:copy>

        </xsl:for-each>

      </MediaYear>

    </xsl:for-each>

  </xsl:template>

</xsl:stylesheet>

但是这段代码只适用于第一级排序,所以我想通过添加这段代码来添加第二级:

<xsl:key match="//File[@MediaArtists != '']" name="AlbumArtists" use="@MediaArtists"/>

<xsl:template match="/">

  <xsl:variable name="VPass1">

    <xsl:call-template name="YearProcess"/>

  </xsl:variable>

  <xsl:apply-templates mode="Artist_Display" select="ext:node-set($VPass1)"/>

</xsl:template>

我将现有的xsl:模板更改为以下内容:

<xsl:template match="/" name="YearProcess" mode="Year_Display">

我添加了以下模板:

<xsl:template match="/" mode="Artist_Display">

  <xsl:for-each select="//File[generate-id(.)= generate-id(key('AlbumArtists', @MediaArtists)[1])]">

    <xsl:sort select="ancestor::MediaYear[1]/@Year"/>

    <xsl:sort select="@MediaArtists"/>

    <MediaArtists>

      <xsl:attribute name="AlbumArtists">

        <xsl:value-of select="@MediaArtists"/>

      </xsl:attribute>

      <xsl:attribute name="AlbumYear">

        <xsl:value-of select="ancestor::MediaYear[1]/@Year"/>

      </xsl:attribute>

      <xsl:for-each select="key('AlbumArtists', @MediaArtists)">

        <xsl:copy>

          <!--<xsl:copy-of select="ancestor::MediaYear[1]"/>-->

          <xsl:copy-of select="node() | @* | node()"/>

        </xsl:copy>

      </xsl:for-each>

    </MediaArtists>

  </xsl:for-each>

</xsl:template>

我没有得到预期的结果,我对XSLT很新,请原谅我,如果答案已经存在,我搜索但我有点迷失,我不知道到底要搜索什么。< / p>

我很确定它不是很清楚,如果需要,代码可以在这里找到:

https://github.com/jaguar0076/FileManager/blob/master/FileManager/Stylesheet.xslt

我不知道去哪里,也许muenchian方法不是达到我想要的最佳方式。

感谢您提供的任何帮助,

罗德里格

编辑:标题已更改

2 个答案:

答案 0 :(得分:2)

这是一个基于创建三个键的解决方案         

  <xsl:output method="xml" indent="yes"/>

  <xsl:strip-space elements="*" />

  <xsl:key match="File[@MediaYear != 0]" name="MediaYears" use="@MediaYear"/>

  <!-- the following key has been designed on the assumption that MediaYear never contains a space-->
  <xsl:key match="File[@MediaArtists != '']" name="MediaArtists" use="concat(@MediaYear, ' ', @MediaArtists)"/>

  <!-- the following key has been designed on the assumption that MediaYear never contains a space
  AND MediaArtists never contains a %-->
  <xsl:key match="File[@MediaAlbum != '']" name="MediaAlbum" use="concat(@MediaYear, ' ', @MediaArtists, '%', @MediaAlbum)"/>

  <xsl:template match="/">

    <xsl:for-each select="//File[generate-id(.)= generate-id(key('MediaYears', @MediaYear)[1])]">

      <xsl:sort select="@MediaYear"/>

      <MediaYear Year="{@MediaYear}">

        <xsl:variable name="MediaYear" select="@MediaYear"/>
        <xsl:for-each select="//File[@MediaYear=$MediaYear and generate-id(.)= generate-id(key('MediaArtists', concat(@MediaYear, ' ', @MediaArtists))[1])]">

          <xsl:sort select="@MediaArtists"/>
          <MediaArtists Artist="{@MediaArtists}">
            <xsl:variable name="MediaArtists" select="@MediaArtists"/>

            <xsl:for-each select="//File[@MediaYear=$MediaYear and @MediaArtists=$MediaArtists and generate-id(.)= generate-id(key('MediaAlbum', concat(@MediaYear, ' ', @MediaArtists, '%', @MediaAlbum))[1])]">
              <xsl:sort select="@MediaAlbum"/>

              <MediaAlbum Album="{@MediaAlbum}">
                <xsl:variable name="MediaAlbum" select="@MediaAlbum"/>
                <xsl:for-each select="//File[@MediaYear=$MediaYear and @MediaArtists=$MediaArtists and @MediaAlbum=$MediaAlbum]">
                  <xsl:copy>

                    <xsl:copy-of select="node() | @* | node()"/>

                  </xsl:copy>
                </xsl:for-each>
              </MediaAlbum>
            </xsl:for-each>
          </MediaArtists>
        </xsl:for-each>

      </MediaYear>

    </xsl:for-each>


  </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

如果您的处理器支持EXSLT(就像您的代码中所示),您可以使用set:distinct()函数而不是Muenchian分组。例如,像:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:set="http://exslt.org/sets" 
extension-element-prefixes="set">

<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="fileByYear" match="File" use="@MediaYear" />
<xsl:key name="fileByArtistYear" match="File" use="concat(@MediaArtists, '|', @MediaYear)" />
<xsl:key name="fileByAlbumArtistYear" match="File" use="concat(@MediaAlbum, '|', @MediaArtists, '|', @MediaYear)" />

<xsl:template match="/">

<xsl:for-each select="set:distinct(//File/@MediaYear)">
<xsl:sort select="." data-type="number" order="ascending"/>
<MediaYear Year="{.}">
    <xsl:for-each select="set:distinct(key('fileByYear', .)/@MediaArtists)">
    <xsl:sort select="." data-type="text" order="ascending"/>
    <MediaArtists Artist="{.}">
        <xsl:for-each select="set:distinct(key('fileByArtistYear', concat(., '|', ../@MediaYear))/@MediaAlbum)">
        <xsl:sort select="." data-type="text" order="ascending"/>
        <MediaAlbum Album="{.}">
            <xsl:for-each select="key('fileByAlbumArtistYear', concat(., '|', ../@MediaArtists, '|', ../@MediaYear))">
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </MediaAlbum>
        </xsl:for-each>
    </MediaArtists>
    </xsl:for-each>
</MediaYear>
</xsl:for-each>

</xsl:template>
</xsl:stylesheet>

请注意,如果您的目录已经是特定年份,您可以使用它来保存一些处理。