在XSLT中的表上的递归

时间:2014-06-10 00:17:59

标签: xml xslt recursion

我的XML是几百个电影元素,并且我试图为每个类型创建一个表,其中表的每一行都是SET元素,每个元素包含一个电影。

这是我的xml(大大简化和缩短,在我的真实交易中有100多个'电影'元素)

    <mediaList>
        <movie>
        <title>16 Blocks</title>
        <director>Richard Donner</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Thrillers</subGenre>
            <subGenre>Crime Action</subGenre>
        </genreWrap>
    </movie>

    <movie>
         <title>Armageddon</title>
        <director>Michael Bay</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Deadly Disasters</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>African Queen, The</title>
        <director>John Huston</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Classics</subGenre>
            <subGenre>Adventures</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>Assault on Precinct 13</title>
        <director>Jean-François Richet</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Thrillers</subGenre>
            <subGenre>Crime Action</subGenre>
        </genreWrap>
    </movie>
    <movie>
        <title>Back to the Future 3 (Box Set)</title>
        <director>Robert Zemeckis</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Westerns</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>Billy Budd</title>
        <director>Peter Ustinov</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Classics</subGenre>
            <subGenre>Adventures</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>Black Arrow</title>
        <director>Gordon Douglas</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Adventures</subGenre>
            <subGenre>Super Swashbucklers</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>Black Cat Run</title>
        <director>D.J. Caruso</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Thrillers</subGenre>
        </genreWrap>
    </movie>

    <movie>
         <title>Captains Courageous</title>
        <director>Victor Fleming</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Classics</subGenre>
            <subGenre>Adventures</subGenre>
        </genreWrap>
    </movie>

    <movie>
       <title>Brothers Grimm, The</title>
        <director>Terry Gilliam</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Adventures</subGenre>
        </genreWrap>
    </movie>

    <movie>
        <title>Convoy</title>
        <director>Sam Peckinpah</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Comedies</subGenre>
        </genreWrap>
    </movie>

<movie>
 <title>Deliverance</title>
        <director>John Boorman</director>
        <genreWrap>
            <genre>Action and Adventure</genre>
            <subGenre>Action Classics</subGenre>
            <subGenre>Action Thrillers</subGenre>
            <subGenre>Adventures</subGenre>
        </genreWrap>
</movie>
</mediaList>

和我的样式表

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

    <xsl:key name="genre" match="genre" use="."/>

    <xsl:template match="/">
        <xsl:for-each
            select="mediaList/movie/genreWrap/genre[local-name() = 'genre'][generate-id()=generate-id(key('genre', .)[1])]">
            <xsl:variable name="genre" select="."/>
            <xsl:result-document method="html" href="GenreHTML/{translate(.,' ','')}.html">
                <html>
                    <head>
                        <title>
                            <xsl:value-of select="translate(.,' ','')"/>
                        </title>
                    </head>
                    <body>
                        <table class="genre">
                            <tr>
                                <xsl:variable name="movies">
                                    <xsl:apply-templates select="key('genre', .)/ancestor::movie">
                                        <xsl:sort/>
                                    </xsl:apply-templates>
                                </xsl:variable>
                                <xsl:copy-of select="subsequence($movies/td,1,6)"/>
                            </tr>
                        </table>
                    </body>
                </html>
            </xsl:result-document>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="movie">
        <td>
                <xsl:value-of select="title"/>
        </td>
    </xsl:template>

</xsl:stylesheet>

我知道现在我正在使用这个序列函数,它只打印前6个,我试图弄清楚如何递归这样做,所以它会在每个上面打印6

期望的输出:

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>ActionandAdventure</title>
</head>
   <body>
      <table class="genre">
         <tr>
            <td>16 Blocks</td>
            <td>African Queen, The</td>
            <td>Armageddon</td>
            <td>Assault on Precinct 13</td>
            <td>Back to the Future 3 (Box Set)</td>
            <td>Billy Budd</td>
         </tr>
<tr>
<td>Black Arrow</td>
<td>Black Cat Run</td>
<td>Brothers Grimm, The</td
<td>Captains Courageous</td>
<td>Convoy</td>
<td>Deliverance</td>
</tr>
      </table>
   </body>
</html>

我正在使用XSLT 2.0,但我会带走任何东西!我故意从一种类型中提取所有xml示例,但请确保真正的一部分有许多不同类型的电影

1 个答案:

答案 0 :(得分:1)

如果您使用的是XSLT 2.0,那么就没有必要在这里使用Muenchian分组,虽然功能强大,但您可以使用 xsl:for-each-group

<xsl:for-each-group select="movie" group-by="genreWrap/genre">

然后,分开&#39;将每组电影分成6行,使用 xsl:for-each 选择电影元素,这些元素将作为每行的第一个单元格出现(即第1,第7,第13等职位)

<xsl:for-each select="current-group()[position() mod 6 = 1]">

要选择行中的元素,您可以执行此操作(因为在 xsl:for-each 的上下文中,position()将设置为1,2,3等等,但你需要在实际位置的元素,这将是1,7,13等。)

<xsl:apply-templates 
     select="current-group()[position() >= ($pos - 1) * 6 + 1 and position() &lt; ($pos - 1) * 6 + 7]" />

在XSLT 2.0中,如果你连续拍摄的电影少于6部,你甚至不需要递归。您只需使用 xsl:for-each 循环

即可
<xsl:for-each select="1 to 6 - (count(current-group()) mod 6)">
     <td></td>
</xsl:for-each>

试试这个XSLT

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

    <xsl:template match="/*">
        <xsl:for-each-group select="movie" group-by="genreWrap/genre">
            <xsl:variable name="genre" select="."/>
            <xsl:result-document method="html" href="GenreHTML/{translate(current-grouping-key(),' ','')}.html">
                <html>
                    <head>
                        <title>
                            <xsl:value-of select="translate(current-grouping-key(),' ','')"/>
                        </title>
                    </head>
                    <body>
                        <table class="genre">
                            <xsl:for-each select="current-group()[position() mod 6 = 1]">
                                <xsl:variable name="pos" select="position()" />
                                    <tr>
                                        <xsl:apply-templates select="current-group()[position() >= ($pos - 1) * 6 + 1 and position() &lt; ($pos - 1) * 6 + 7]" />
                                        <xsl:if test="not(current-group()[($pos - 1) * 6 + 6])">
                                            <xsl:for-each select="1 to 6 - (count(current-group()) mod 6)">
                                                <td></td>
                                            </xsl:for-each>
                                        </xsl:if>
                                    </tr>
                            </xsl:for-each>
                        </table>
                    </body>
                </html>
            </xsl:result-document>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="movie">
        <td>
            <xsl:value-of select="title"/>
        </td>
    </xsl:template>
</xsl:stylesheet>
编辑:我在这个答案中没有考虑到排序。如果你想要对电影进行排序,那么目前我能想到的唯一方法是对它们进行排序,并将结果存储在变量中,然后像以前一样迭代它。

试试这个XSLT(它还应考虑标题的区分大小写,但以大写字母对标题进行排序):

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

    <xsl:template match="/*">
        <xsl:for-each-group select="movie" group-by="genreWrap/genre">
            <xsl:variable name="genre" select="."/>
            <result-document method="html" href="GenreHTML/{translate(current-grouping-key(),' ','')}.html">
                <html>
                    <head>
                        <title>
                            <xsl:value-of select="translate(current-grouping-key(),' ','')"/>
                        </title>
                    </head>
                    <body>
                        <table class="genre">
                            <xsl:variable name="sorted-group">
                                <xsl:for-each select="current-group()">
                                    <xsl:sort select="upper-case(title)" />
                                    <xsl:copy-of select="." />
                                </xsl:for-each>
                            </xsl:variable>
                            <xsl:for-each select="$sorted-group/movie[position() mod 6 = 1]">
                                <xsl:variable name="pos" select="position()" />
                                <xsl:apply-templates select="$sorted-group/movie[6]" />
                                <tr>
                                    <xsl:for-each select="$sorted-group/movie[position() >= ($pos - 1) * 6 + 1 and position() &lt; ($pos - 1) * 6 + 7]">
                                        <td>
                                            <xsl:value-of select="title"/>
                                        </td>
                                    </xsl:for-each>
                                    <xsl:if test="not($sorted-group/movie[($pos - 1) * 6 + 6])">
                                        <xsl:for-each select="1 to 6 - (count($sorted-group/movie) mod 6)">
                                            <td></td>
                                        </xsl:for-each>
                                    </xsl:if>
                                </tr>
                            </xsl:for-each>
                        </table>
                    </body>
                </html>
            </result-document>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>