在XSLT中按月和年分组XML节点

时间:2022-01-19 23:41:37

Update

I'd like to appologize to the people who provided answers, I seem to have caused all sorts of confusion. To avoid complicating things even further, I've removed the previous code and have added new information. Read on...

我想向提供答案的人道歉,我似乎引起了各种各样的困惑。为了避免使事情进一步复杂化,我删除了以前的代码并添加了新信息。继续阅读......

I'm working on a custom Blog in Umbraco. Umbraco spits out XML as the output which is then read using XSLT.

我正在开发Umbraco的自定义博客。 Umbraco将XML作为输出吐出,然后使用XSLT读取。

The structure of the XML is as follows

XML的结构如下

  • Blog
    • Blog Centre
      • Room
        • Blog Post
        • Blog Post
        • Blog Post
      • 房间博客发布博客发布博客帖子

      • Room
        • Blog Post
      • 房间博客文章

    • 博客中心室博客发布博客发布博客发布室博客帖子

    • Blog Centre
      • Room
        • Blog Post
      • 房间博客文章

    • 博客中心室博客文章

  • 博客博客中心室博客帖子博客博客博客室博客博客博客中心室博客帖子

Here's the XML code, I've cleaned up a lot of it to make it at least somewhat readable.

这是XML代码,我已经清理了很多,以使其至少具有一定的可读性。

<Blog id="1078" parentID="1049" level="2" writerID="0" creatorID="0" nodeType="1073" template="1089" sortOrder="7" createDate="2010-09-27T14:11:04" updateDate="2010-10-12T16:59:12" nodeName="Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078" isDoc="">
    <newPageTitle>The Lorem Ipsum Blog</newPageTitle>
    <BlogCentre id="1079" parentID="1078" level="3" writerID="0" creatorID="0" nodeType="1075" template="1076" sortOrder="1" createDate="2010-09-27T14:11:49" updateDate="2010-10-07T14:43:13" nodeName="Blog Centre 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079" isDoc="">
        <Room id="1081" parentID="1079" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:26" updateDate="2010-10-07T14:43:06" nodeName="Room 10" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081" isDoc="">
            <BlogPost id="1175" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1192" sortOrder="1" createDate="2010-10-07T14:51:48" updateDate="2010-10-12T21:30:53" nodeName="The first ever Blog post" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1175" isDoc="">
                <topicTitle>The first ever blog</topicTitle>
            </BlogPost>
            <BlogPost id="1180" parentID="1081" level="5" writerID="0" creatorID="3" nodeType="1087" template="1089" sortOrder="2" createDate="2010-10-08T15:52:20" updateDate="2010-10-12T16:57:00" nodeName="asdasd" writerName="Administrator" creatorName="ZX" path="-1,1049,1078,1079,1081,1180" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
            <BlogPost id="1181" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="3" createDate="2010-10-08T17:50:19" updateDate="2010-10-12T11:40:37" nodeName="condimentum" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1181" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-01T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1194" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="4" createDate="2010-10-12T11:41:50" updateDate="2010-10-12T11:42:37" nodeName="Nam augue" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1194" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-05T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1195" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="5" createDate="2010-10-12T11:42:15" updateDate="2010-10-12T11:42:25" nodeName="consequat nunc" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1195" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-12T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1196" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="6" createDate="2010-10-12T12:05:57" updateDate="2010-10-12T12:08:40" nodeName="cursus congue" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1196" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-10-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1197" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="7" createDate="2010-10-12T12:08:54" updateDate="2010-10-12T12:09:24" nodeName="inceptos" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1197" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-11-19T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1198" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="8" createDate="2010-10-12T12:09:45" updateDate="2010-10-12T12:10:13" nodeName="inceptos himenaeos" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1198" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-12-16T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1199" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="9" createDate="2010-10-12T12:10:29" updateDate="2010-10-12T12:10:56" nodeName="consequat" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1199" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-01-13T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1200" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="10" createDate="2010-10-12T12:11:08" updateDate="2010-10-12T12:11:35" nodeName="himenaeos" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1200" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-02-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1201" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="11" createDate="2010-10-12T12:11:45" updateDate="2010-10-12T12:12:35" nodeName="cursus congue" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1201" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-04-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1202" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="12" createDate="2010-10-12T12:12:18" updateDate="2010-10-12T12:12:45" nodeName="pharetra" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1202" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-03-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1203" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="13" createDate="2010-10-12T12:13:05" updateDate="2010-10-12T12:13:27" nodeName="inceptos himenaeos" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1203" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-05-26T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1204" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="14" createDate="2010-10-12T12:13:36" updateDate="2010-10-12T12:13:56" nodeName="pharetra" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1204" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-11T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1205" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="15" createDate="2010-10-12T12:14:06" updateDate="2010-10-12T12:14:41" nodeName="Fusce augue" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1205" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-07-08T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1206" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="16" createDate="2010-10-12T12:14:52" updateDate="2010-10-12T12:15:19" nodeName="pharetra et fermentum" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1206" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1207" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="17" createDate="2010-10-12T12:15:31" updateDate="2010-10-12T12:15:51" nodeName="Fusce augue purus" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1207" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-14T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1208" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="18" createDate="2010-10-12T12:16:25" updateDate="2010-10-12T12:16:45" nodeName="Class aptent taciti" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1208" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-04T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1209" parentID="1081" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="19" createDate="2010-10-12T12:17:01" updateDate="2010-10-12T12:17:29" nodeName="Class aptent" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081,1209" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-21T00:00:00</archiveUnder>
            </BlogPost>
        </Room>
        <Room id="1082" parentID="1079" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="2" createDate="2010-09-27T14:12:33" updateDate="2010-10-07T14:43:09" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1082" isDoc="">
            <BlogPost id="1182" parentID="1082" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:51:19" updateDate="2010-10-08T17:51:58" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1082,1182" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1083" parentID="1079" level="4" writerID="0" creatorID="0" nodeType="1077" template="1089" sortOrder="3" createDate="2010-09-27T14:12:40" updateDate="2010-10-07T14:49:48" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1083" isDoc="">
            <BlogPost id="1183" parentID="1083" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:52:22" updateDate="2010-10-08T17:52:39" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1083,1183" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
    <BlogCentre id="1080" parentID="1078" level="3" writerID="0" creatorID="0" nodeType="1075" template="1076" sortOrder="2" createDate="2010-09-27T14:11:55" updateDate="2010-10-07T14:43:23" nodeName="Blog Centre 2" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080" isDoc="">
        <Room id="1084" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:45" updateDate="2010-10-07T14:43:17" nodeName="Room 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1084" isDoc="">
            <BlogPost id="1184" parentID="1084" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:53:05" updateDate="2010-10-08T17:53:29" nodeName="Blog Post 3" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1084,1184" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1085" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="2" createDate="2010-09-27T14:12:50" updateDate="2010-10-07T14:43:19" nodeName="Room 2" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1085" isDoc="">
            <BlogPost id="1185" parentID="1085" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:53:51" updateDate="2010-10-08T17:54:15" nodeName="Blog Post 109" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1085,1185" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1086" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="1089" sortOrder="3" createDate="2010-09-27T14:12:55" updateDate="2010-10-07T14:50:39" nodeName="Room 3" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1086" isDoc="">
            <BlogPost id="1186" parentID="1086" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:54:28" updateDate="2010-10-08T17:54:51" nodeName="Blog Post 123" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1086,1186" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
</Blog>

In the Umbraco XSLT stylesheets, there is a parameter that gets passed for the current page.

在Umbraco XSLT样式表中,有一个参数可以传递给当前页面。

<xsl:param name="currentPage" />

This will always be the parent node <Blog />, so we have to start form here.

这将始终是父节点 ,因此我们必须在此处启动表单。

You'll notice that there are intermediate nodes between <Blog /> and <BlogPost /> but we want to count the TOTAL number of BlogPosts in every <BlogCentre /> and <Room />.

您会注意到 之间存在中间节点,但我们想要计算每个 中的总BlogPost数量。

To do this, I've been using

为此,我一直在使用

<xsl:for-each select="$currentPage/descendant::BlogPost" />

Which selects all the Blog Posts, regardless of Centre/Room.

选择所有博客帖子,无论中心/房间。

Now the requirement I have is, to group each of these Blog Posts by Month and Year (including a Post count for each month). The date I'd like to group them by is the attribute createDate UNLESS there is a child node called <archiveUnder>some-date-here</archiveUnder>. To explain further

现在我的要求是按月和年份对每个博客帖子进行分组(包括每个月的帖子计数)。我想要将它们分组的日期是属性createDate,除非有一个名为 some-date-here 的子节点。进一步解释

Update
I can easily do this part by checking if an attribute is blank, so the solution can omit this part if need-be.

更新我可以通过检查属性是否为空来轻松完成此部分,因此如果需要,解决方案可以省略此部分。

<BlogPost createDate="2010-10-08T17:52:22">
    <!-- no archiveUnder -->
</BlogPost>

^ Use create Date

^使用创建日期

<BlogPost createDate="2010-10-08T17:52:22">
    <archiveUnder>2010-10-08T17:51:19</archiveUnder>
</BlogPost>

^ Use archiveUnder

^使用archiveUnder

Finally, here's an example of the expected output.

最后,这是预期输出的一个例子。

<ul>
    <li>
        <h3>2010</h3>
        <ul>
            <li>September (4)</li>
            <li>August (2)</li>
            <li>June (5)</li> <!-- No July because there are no posts -->
        </ul>
    </li>
    <li>
        <h3>2009</h3>
        <ul>
            <li>April (4)</li>
            <li>March (2)</li>
            <li>January (5)</li> <!-- No February because there are no posts -->
        </ul>
    </li>
</ul>

3 个解决方案

#1


3  

This transformation (141 lines but formatted for readability):

此转换(141行,但格式化为可读性):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:m="my:months" exclude-result-prefixes="m" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="currentPage" select="/*"/>

 <m:months>
   <m>January</m>
   <m>February</m>
   <m>March</m>
   <m>April</m>
   <m>May</m>
   <m>June</m>
   <m>July</m>
   <m>August</m>
   <m>September</m>
   <m>October</m>
   <m>November</m>
   <m>December</m>
 </m:months>

 <xsl:variable name="vMonthNames" select=
  "document('')/*/m:months/*"/>

 <xsl:key name="kPostsByYear" match="BlogPost"
  use="substring-before(
                    concat(archiveUnder,
                           @createDate[not(archiveUnder)]
                           ),
                    '-'
                        )"/>

 <xsl:key name="kPostsByYearMonth" match="BlogPost"
  use="substring(
                 concat(archiveUnder,
                        @createDate[not(archiveUnder)]
                        ),
                 1,7
                 )"/>

 <xsl:template match="/">
   <ul>
    <xsl:apply-templates mode="year" select=
     "$currentPage/*/*/BlogPost
          [generate-id()
          =
           generate-id(key('kPostsByYear',
                       substring-before(
                       concat(archiveUnder,
                              @createDate[not(archiveUnder)]
                              ),
                              '-'
                                         )
                            )[1]
                       )
              ]
     ">
       <xsl:sort order="descending" select=
        "substring-before(
                    concat(archiveUnder,
                           @createDate[not(archiveUnder)]
                           ),

                           '-'
                          )
        "/>
   </xsl:apply-templates>
  </ul>
 </xsl:template>

 <xsl:template match="BlogPost" mode="year">
  <xsl:variable name="vYear" select=
    "substring-before(
                     concat(archiveUnder,
                            @createDate[not(archiveUnder)]
                            ),

                      '-')
    "/>
  <xsl:variable name="vyearBlogs"
                select="key('kPostsByYear',$vYear)"/>
  <li>
    <h3><xsl:value-of select="$vYear"/></h3>
    <ul>
      <xsl:apply-templates mode="month" select=
          "$vyearBlogs
             [generate-id()
             =
              generate-id(key('kPostsByYearMonth',
                          substring(
                        concat(archiveUnder,
                               @createDate[not(archiveUnder)]
                               ),

                        1,7
                                    )
                              )[1]
                          )
              ]
          ">
         <xsl:sort order="descending" select=
            "substring(
                 concat(archiveUnder,
                        @createDate[not(archiveUnder)]
                        ),

                 6,2)"
         />
      </xsl:apply-templates>
   </ul>
  </li>
 </xsl:template>

 <xsl:template match="BlogPost" mode="month">
  <xsl:variable name="vMonth" select=
   "substring(
        concat(archiveUnder,
               @createDate[not(archiveUnder)]
               ),

        6,2)"/>

  <xsl:variable name="vmonthsBlogs" select=
    "key('kPostsByYearMonth',
         substring(
              concat(archiveUnder,
                     @createDate[not(archiveUnder)]
                     ),

              1,7)
         )"/>
  <li><xsl:value-of select=
        "concat($vMonthNames[position()=$vMonth],
                ' (',
                count($vmonthsBlogs),
                ')'
                )"/>
  </li>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided sample XML document (we are accessing every node off the $currentPage <xsl:param> as this will be in the real Umbraco case), formatted for readability and the createDate attribute of every BlogPost moved on the firs line after the element name:

当应用于提供的示例XML文档时(我们正在访问$ currentPage

<Blog id="1078" parentID="1049" level="2" writerID="0" creatorID="0" nodeType="1073" template="1089" sortOrder="7" createDate="2010-09-27T14:11:04" updateDate="2010-10-12T16:59:12" nodeName="Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078" isDoc="">
    <newPageTitle>The Lorem Ipsum Blog</newPageTitle>
    <BlogCentre id="1079" parentID="1078" level="3" writerID="0" creatorID="0" nodeType="1075" template="1076" sortOrder="1" createDate="2010-09-27T14:11:49" updateDate="2010-10-07T14:43:13" nodeName="Blog Centre 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079" isDoc="">
        <Room id="1081" parentID="1079" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:26" updateDate="2010-10-07T14:43:06" nodeName="Room 10" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081" isDoc="">
            <BlogPost id="1175" parentID="1081" level="5"
            createDate="2010-10-07T14:51:48"
            writerID="0" creatorID="0" nodeType="1087"
            template="1192" sortOrder="1"
            updateDate="2010-10-12T21:30:53"
            nodeName="The first ever Blog post"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1175" isDoc="">
                <topicTitle>The first ever blog</topicTitle>
                <archiveUnder/>
            </BlogPost>
            <BlogPost id="1180" parentID="1081"
             createDate="2010-10-08T15:52:20"
             level="5" writerID="0" creatorID="3"
             nodeType="1087" template="1089"
             sortOrder="2"
             updateDate="2010-10-12T16:57:00" nodeName="asdasd"
             writerName="Administrator" creatorName="ZX"
             path="-1,1049,1078,1079,1081,1180" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
            <BlogPost id="1181" parentID="1081" level="5"
             createDate="2010-10-08T17:50:19"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="3"
             updateDate="2010-10-12T11:40:37"
             nodeName="condimentum"
             writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1181" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-01T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1194" parentID="1081" level="5"
            createDate="2010-10-12T11:41:50"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="4"
            updateDate="2010-10-12T11:42:37"
            nodeName="Nam augue" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1194" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-05T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1195" parentID="1081" level="5"
            createDate="2010-10-12T11:42:15"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="5"
            updateDate="2010-10-12T11:42:25"
            nodeName="consequat nunc"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1195" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-12T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1196" parentID="1081" level="5"
            createDate="2010-10-12T12:05:57"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="6"
            updateDate="2010-10-12T12:08:40"
            nodeName="cursus congue"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1196" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-10-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1197" parentID="1081" level="5"
             createDate="2010-10-12T12:08:54"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="7"
             updateDate="2010-10-12T12:09:24"
             nodeName="inceptos" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1197" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-11-19T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1198" parentID="1081" level="5"
            createDate="2010-10-12T12:09:45"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="8"
            updateDate="2010-10-12T12:10:13"
            nodeName="inceptos himenaeos"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1198" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-12-16T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1199" parentID="1081" level="5"
             createDate="2010-10-12T12:10:29"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="9"
             updateDate="2010-10-12T12:10:56"
             nodeName="consequat" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1199" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-01-13T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1200" parentID="1081" level="5"
            createDate="2010-10-12T12:11:08"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="10"
            updateDate="2010-10-12T12:11:35"
            nodeName="himenaeos" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1200" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-02-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1201" parentID="1081" level="5"
             createDate="2010-10-12T12:11:45"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="11"
             updateDate="2010-10-12T12:12:35"
             nodeName="cursus congue" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1201" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-04-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1202" parentID="1081" level="5"
            createDate="2010-10-12T12:12:18"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="12"
            updateDate="2010-10-12T12:12:45" nodeName="pharetra"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1202" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-03-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1203" parentID="1081" level="5"
            createDate="2010-10-12T12:13:05"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="13"
            updateDate="2010-10-12T12:13:27"
            nodeName="inceptos himenaeos"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1203" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-05-26T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1204" parentID="1081" level="5"
            createDate="2010-10-12T12:13:36"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="14"
            updateDate="2010-10-12T12:13:56"
            nodeName="pharetra" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1204" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-11T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1205" parentID="1081" level="5"
            createDate="2010-10-12T12:14:06"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="15"
            updateDate="2010-10-12T12:14:41"
            nodeName="Fusce augue" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1205" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-07-08T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1206" parentID="1081" level="5"
            createDate="2010-10-12T12:14:52"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="16"
            updateDate="2010-10-12T12:15:19"
            nodeName="pharetra et fermentum"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1206" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1207" parentID="1081"
            createDate="2010-10-12T12:15:31"
            level="5" writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="17"
            updateDate="2010-10-12T12:15:51"
            nodeName="Fusce augue purus" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1207" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-14T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1208" parentID="1081" level="5"
            createDate="2010-10-12T12:16:25"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="18"
            updateDate="2010-10-12T12:16:45"
            nodeName="Class aptent taciti" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1208" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-04T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1209" parentID="1081" level="5"
            createDate="2010-10-12T12:17:01"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="19"
            updateDate="2010-10-12T12:17:29" nodeName="Class aptent"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1209" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-21T00:00:00</archiveUnder>
            </BlogPost>
        </Room>
        <Room id="1082" parentID="1079" level="4"
        createDate="2010-09-27T14:12:33"
        writerID="0" creatorID="0" nodeType="1077" template="0"
        sortOrder="2"
        updateDate="2010-10-07T14:43:09" nodeName="Test Blog"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1079,1082" isDoc="">
            <BlogPost id="1182" parentID="1082" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:51:19" updateDate="2010-10-08T17:51:58" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1082,1182" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1083" parentID="1079" level="4" writerID="0"
        createDate="2010-09-27T14:12:40"
        creatorID="0" nodeType="1077" template="1089" sortOrder="3"
        updateDate="2010-10-07T14:49:48" nodeName="Test Blog"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1079,1083" isDoc="">
            <BlogPost id="1183" parentID="1083" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:52:22" updateDate="2010-10-08T17:52:39" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1083,1183" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
    <BlogCentre id="1080" parentID="1078" level="3" writerID="0"
    createDate="2010-09-27T14:11:55"
    creatorID="0" nodeType="1075" template="1076" sortOrder="2"
    updateDate="2010-10-07T14:43:23" nodeName="Blog Centre 2"
    writerName="Administrator" creatorName="Administrator"
    path="-1,1049,1078,1080" isDoc="">
        <Room id="1084" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:45" updateDate="2010-10-07T14:43:17" nodeName="Room 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1084" isDoc="">
            <BlogPost id="1184" parentID="1084" level="5" writerID="0"
            createDate="2010-10-08T17:53:05"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:53:29" nodeName="Blog Post 3"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1080,1084,1184" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1085" parentID="1080" level="4" writerID="0"
        createDate="2010-09-27T14:12:50"
        creatorID="0" nodeType="1077" template="0" sortOrder="2"
        updateDate="2010-10-07T14:43:19" nodeName="Room 2"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1080,1085" isDoc="">
            <BlogPost id="1185" parentID="1085" level="5" writerID="0"
            createDate="2010-10-08T17:53:51"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:54:15" nodeName="Blog Post 109"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1080,1085,1185" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1086" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="1089" sortOrder="3" createDate="2010-09-27T14:12:55" updateDate="2010-10-07T14:50:39" nodeName="Room 3" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1086" isDoc="">
            <BlogPost id="1186" parentID="1086" level="5" writerID="0"
            createDate="2010-10-08T17:54:28"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:54:51"
            nodeName="Blog Post 123" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1080,1086,1186" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
</Blog>

produces the wanted, correct result:

产生想要的,正确的结果:

<ul>
   <li>
      <h3>2010</h3>
      <ul>
         <li>October (7)</li>
         <li>September (2)</li>
         <li>August (3)</li>
         <li>July (1)</li>
         <li>June (3)</li>
         <li>May (1)</li>
         <li>April (1)</li>
         <li>March (1)</li>
         <li>February (1)</li>
         <li>January (1)</li>
      </ul>
   </li>
   <li>
      <h3>2009</h3>
      <ul>
         <li>December (1)</li>
         <li>November (1)</li>
         <li>October (1)</li>
      </ul>
   </li>
</ul>

Do note:

  1. Muenchian Grouping is used -- both for determining the different years and for determining the different months for each year.

    使用Muenchian Grouping - 用于确定不同的年份和确定每年的不同月份。

  2. The date is chosen between the createDate attribute and the archiveUnder child using the expression:

    使用表达式在createDate属性和archiveUnder子节点之间选择日期:

    concat(archiveUnder, @createDate[not(archiveUnder)])

This concatenation only picks @createDate if archiveUnder is missing or empty.

如果archiveUnder缺失或为空,则此连接仅选择@createDate。

.3. The transformation will produce the correct result even if the dates of the elements are unsorted.

0.3。即使元素的日期未排序,转换也会产生正确的结果。

#2


2  

As described by Jeni Tennison this would be a two-step method:

正如Jeni Tennison所描述的那样,这将是一个两步法:

  1. Identify the months with blog posts.
  2. 通过博客文章确定月份。

  3. Get all the posts for a particular month.
  4. 获取特定月份的所有帖子。

We can get the first part using the following XPath expression:

我们可以使用以下XPath表达式获取第一部分:

BlogPost[not (substring(@createDate, 1, 7) 
    = substring(preceding-sibling::*[1]/@createDate, 1, 7))]

Then, for part two, we need to count the posts using the count() function:

然后,对于第二部分,我们需要使用count()函数计算帖子:

BlogPost[not (substring(@createDate, 1, 7) 
    = substring(preceding-sibling::*[1]/@createDate, 1, 7))]

In our XSLT, we can use the expressions as follows:

在我们的XSLT中,我们可以使用如下表达式:

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

  <xsl:template match="Room">
    <ul>
      <xsl:for-each select="BlogPost[not (substring(@createDate, 1, 7) = substring(preceding-sibling::*[1]/@createDate, 1, 7))]">
        <xsl:sort select="@createDate" order="descending"/>
        <li>
          <xsl:value-of select="substring(@createDate, 1, 7)"/>
          <xsl:text>(</xsl:text>
          <xsl:value-of select="count(//BlogPost[substring(@createDate, 1, 7) = substring(current()/@createDate, 1, 7)]) "/>
          <xsl:text>)</xsl:text>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

</xsl:stylesheet>

The stylesheet above simply works month-based and doesn't consider multiple years. To support years, you can add an additional step to get the unique years first:

上面的样式表只是基于月份工作,不考虑多年。为了支持多年,您可以添加一个额外的步骤来获得独特的年份:

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

  <xsl:template match="Room">
    <ul>
      <xsl:for-each select="BlogPost[not (substring(@createDate, 1, 4) = substring(preceding-sibling::*[1]/@createDate, 1, 4))]">
        <xsl:sort select="@createDate" order="descending"/>
        <li>
          <xsl:value-of select="substring(@createDate, 1, 4)"/>
          <ul>
            <xsl:for-each select="//BlogPost[substring(@createDate, 1, 4) = substring(current()/@createDate, 1, 4) and not (substring(@createDate, 1, 7) = substring(preceding-sibling::*[1]/@createDate, 1, 7))]">
              <xsl:sort select="@createDate" order="descending"/>
              <li>
                <xsl:value-of select="substring(@createDate, 1, 7)"/>
                <xsl:text>(</xsl:text>
                <xsl:value-of select="count(//BlogPost[substring(@createDate, 1, 7) = substring(current()/@createDate, 1, 7)]) "/>
                <xsl:text>)</xsl:text>
              </li>
            </xsl:for-each>
          </ul>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

</xsl:stylesheet>

#3


2  

This stylesheet:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:m="month"
 exclude-result-prefixes="m">
    <xsl:param name="currentPage" select="/Blog"/>
    <xsl:key name="kBlogPostByYear" match="BlogPost"
             use="substring((@createDate|archiveUnder)[last()],1,4)"/>
    <xsl:key name="kBlogPostByYearMonth" match="BlogPost"
             use="substring((@createDate|archiveUnder)[last()],1,7)"/>
    <m:month>January</m:month>
    <m:month>February</m:month>
    <m:month>March</m:month>
    <m:month>April</m:month>
    <m:month>May</m:month>
    <m:month>June</m:month>
    <m:month>July</m:month>
    <m:month>August</m:month>
    <m:month>September</m:month>
    <m:month>October</m:month>
    <m:month>November</m:month>
    <m:month>December</m:month>
    <xsl:variable name="vMonth" select="document('')/*/m:*"/>
    <xsl:template match="/">
        <ul>
            <xsl:apply-templates
                 select="$currentPage/*/*/BlogPost
                                 [count(.|key('kBlogPostByYear',
                                              substring((@createDate|
                                                         archiveUnder)
                                                         [last()],
                                                        1,
                                                        4))[1])=1]">
                <xsl:sort select="substring((@createDate|archiveUnder)
                                            [last()],
                                            1,4)" order="descending"/>
            </xsl:apply-templates>
        </ul>
    </xsl:template>
    <xsl:template match="BlogPost">
        <xsl:variable name="vYear" select="substring((@createDate|
                                                      archiveUnder)[last()],
                                                      1,
                                                      4)"/>
        <ul>
            <h3>
                <xsl:value-of select="$vYear"/>
            </h3>
            <ul>
                <xsl:apply-templates
                     select="$currentPage/*/*/BlogPost
                                         [generate-id() =
                                          generate-id(
                                             key('kBlogPostByYearMonth',
                                                 concat($vYear,'-',
                                                        substring(
                                                           (@createDate|
                                                            archiveUnder)
                                                                 [last()],
                                                           6,
                                                           2))))]"
                                     mode="month">
                    <xsl:sort select="substring((@createDate|archiveUnder)
                                                [last()],
                                                6,2)" order="descending"/>
                </xsl:apply-templates>
            </ul>
        </ul>
    </xsl:template>
        <xsl:template match="BlogPost" mode="month">
        <xsl:variable name="vDate"
                      select="(@createDate|archiveUnder)[last()]"/>
        <li>
            <xsl:value-of select="concat($vMonth
                                            [number(
                                               substring($vDate,
                                                         6,
                                                         2))],
                                         ' (',
                                         count(key('kBlogPostByYearMonth',
                                                   substring($vDate,1,7))),
                                         ')')"/>
        </li>
    </xsl:template>
</xsl:stylesheet>

Output:

<ul>
    <ul>
        <h3>2010</h3>
        <ul>
            <li>October (7)</li>
            <li>September (2)</li>
            <li>August (3)</li>
            <li>July (1)</li>
            <li>June (3)</li>
            <li>May (1)</li>
            <li>April (1)</li>
            <li>March (1)</li>
            <li>February (1)</li>
            <li>January (1)</li>
        </ul>
    </ul>
    <ul>
        <h3>2009</h3>
        <ul>
            <li>December (1)</li>
            <li>November (1)</li>
            <li>October (1)</li>
        </ul>
    </ul>
</ul>

Edit 3: Sorry, I've missed the sorting. Now added. Also, minor refactor with a $currentPage param.

编辑3:对不起,我错过了排序。现在补充说。另外,带有$ currentPage参数的次要重构。

#1


3  

This transformation (141 lines but formatted for readability):

此转换(141行,但格式化为可读性):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:m="my:months" exclude-result-prefixes="m" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="currentPage" select="/*"/>

 <m:months>
   <m>January</m>
   <m>February</m>
   <m>March</m>
   <m>April</m>
   <m>May</m>
   <m>June</m>
   <m>July</m>
   <m>August</m>
   <m>September</m>
   <m>October</m>
   <m>November</m>
   <m>December</m>
 </m:months>

 <xsl:variable name="vMonthNames" select=
  "document('')/*/m:months/*"/>

 <xsl:key name="kPostsByYear" match="BlogPost"
  use="substring-before(
                    concat(archiveUnder,
                           @createDate[not(archiveUnder)]
                           ),
                    '-'
                        )"/>

 <xsl:key name="kPostsByYearMonth" match="BlogPost"
  use="substring(
                 concat(archiveUnder,
                        @createDate[not(archiveUnder)]
                        ),
                 1,7
                 )"/>

 <xsl:template match="/">
   <ul>
    <xsl:apply-templates mode="year" select=
     "$currentPage/*/*/BlogPost
          [generate-id()
          =
           generate-id(key('kPostsByYear',
                       substring-before(
                       concat(archiveUnder,
                              @createDate[not(archiveUnder)]
                              ),
                              '-'
                                         )
                            )[1]
                       )
              ]
     ">
       <xsl:sort order="descending" select=
        "substring-before(
                    concat(archiveUnder,
                           @createDate[not(archiveUnder)]
                           ),

                           '-'
                          )
        "/>
   </xsl:apply-templates>
  </ul>
 </xsl:template>

 <xsl:template match="BlogPost" mode="year">
  <xsl:variable name="vYear" select=
    "substring-before(
                     concat(archiveUnder,
                            @createDate[not(archiveUnder)]
                            ),

                      '-')
    "/>
  <xsl:variable name="vyearBlogs"
                select="key('kPostsByYear',$vYear)"/>
  <li>
    <h3><xsl:value-of select="$vYear"/></h3>
    <ul>
      <xsl:apply-templates mode="month" select=
          "$vyearBlogs
             [generate-id()
             =
              generate-id(key('kPostsByYearMonth',
                          substring(
                        concat(archiveUnder,
                               @createDate[not(archiveUnder)]
                               ),

                        1,7
                                    )
                              )[1]
                          )
              ]
          ">
         <xsl:sort order="descending" select=
            "substring(
                 concat(archiveUnder,
                        @createDate[not(archiveUnder)]
                        ),

                 6,2)"
         />
      </xsl:apply-templates>
   </ul>
  </li>
 </xsl:template>

 <xsl:template match="BlogPost" mode="month">
  <xsl:variable name="vMonth" select=
   "substring(
        concat(archiveUnder,
               @createDate[not(archiveUnder)]
               ),

        6,2)"/>

  <xsl:variable name="vmonthsBlogs" select=
    "key('kPostsByYearMonth',
         substring(
              concat(archiveUnder,
                     @createDate[not(archiveUnder)]
                     ),

              1,7)
         )"/>
  <li><xsl:value-of select=
        "concat($vMonthNames[position()=$vMonth],
                ' (',
                count($vmonthsBlogs),
                ')'
                )"/>
  </li>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided sample XML document (we are accessing every node off the $currentPage <xsl:param> as this will be in the real Umbraco case), formatted for readability and the createDate attribute of every BlogPost moved on the firs line after the element name:

当应用于提供的示例XML文档时(我们正在访问$ currentPage

<Blog id="1078" parentID="1049" level="2" writerID="0" creatorID="0" nodeType="1073" template="1089" sortOrder="7" createDate="2010-09-27T14:11:04" updateDate="2010-10-12T16:59:12" nodeName="Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078" isDoc="">
    <newPageTitle>The Lorem Ipsum Blog</newPageTitle>
    <BlogCentre id="1079" parentID="1078" level="3" writerID="0" creatorID="0" nodeType="1075" template="1076" sortOrder="1" createDate="2010-09-27T14:11:49" updateDate="2010-10-07T14:43:13" nodeName="Blog Centre 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079" isDoc="">
        <Room id="1081" parentID="1079" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:26" updateDate="2010-10-07T14:43:06" nodeName="Room 10" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1081" isDoc="">
            <BlogPost id="1175" parentID="1081" level="5"
            createDate="2010-10-07T14:51:48"
            writerID="0" creatorID="0" nodeType="1087"
            template="1192" sortOrder="1"
            updateDate="2010-10-12T21:30:53"
            nodeName="The first ever Blog post"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1175" isDoc="">
                <topicTitle>The first ever blog</topicTitle>
                <archiveUnder/>
            </BlogPost>
            <BlogPost id="1180" parentID="1081"
             createDate="2010-10-08T15:52:20"
             level="5" writerID="0" creatorID="3"
             nodeType="1087" template="1089"
             sortOrder="2"
             updateDate="2010-10-12T16:57:00" nodeName="asdasd"
             writerName="Administrator" creatorName="ZX"
             path="-1,1049,1078,1079,1081,1180" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
            <BlogPost id="1181" parentID="1081" level="5"
             createDate="2010-10-08T17:50:19"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="3"
             updateDate="2010-10-12T11:40:37"
             nodeName="condimentum"
             writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1181" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-01T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1194" parentID="1081" level="5"
            createDate="2010-10-12T11:41:50"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="4"
            updateDate="2010-10-12T11:42:37"
            nodeName="Nam augue" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1194" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-05T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1195" parentID="1081" level="5"
            createDate="2010-10-12T11:42:15"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="5"
            updateDate="2010-10-12T11:42:25"
            nodeName="consequat nunc"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1195" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-12T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1196" parentID="1081" level="5"
            createDate="2010-10-12T12:05:57"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="6"
            updateDate="2010-10-12T12:08:40"
            nodeName="cursus congue"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1196" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-10-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1197" parentID="1081" level="5"
             createDate="2010-10-12T12:08:54"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="7"
             updateDate="2010-10-12T12:09:24"
             nodeName="inceptos" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1197" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-11-19T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1198" parentID="1081" level="5"
            createDate="2010-10-12T12:09:45"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="8"
            updateDate="2010-10-12T12:10:13"
            nodeName="inceptos himenaeos"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1198" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2009-12-16T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1199" parentID="1081" level="5"
             createDate="2010-10-12T12:10:29"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="9"
             updateDate="2010-10-12T12:10:56"
             nodeName="consequat" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1199" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-01-13T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1200" parentID="1081" level="5"
            createDate="2010-10-12T12:11:08"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="10"
            updateDate="2010-10-12T12:11:35"
            nodeName="himenaeos" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1200" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-02-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1201" parentID="1081" level="5"
             createDate="2010-10-12T12:11:45"
             writerID="0" creatorID="0" nodeType="1087"
             template="1089" sortOrder="11"
             updateDate="2010-10-12T12:12:35"
             nodeName="cursus congue" writerName="Administrator"
             creatorName="Administrator"
             path="-1,1049,1078,1079,1081,1201" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-04-22T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1202" parentID="1081" level="5"
            createDate="2010-10-12T12:12:18"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="12"
            updateDate="2010-10-12T12:12:45" nodeName="pharetra"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1202" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-03-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1203" parentID="1081" level="5"
            createDate="2010-10-12T12:13:05"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="13"
            updateDate="2010-10-12T12:13:27"
            nodeName="inceptos himenaeos"
            writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1203" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-05-26T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1204" parentID="1081" level="5"
            createDate="2010-10-12T12:13:36"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="14"
            updateDate="2010-10-12T12:13:56"
            nodeName="pharetra" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1204" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-11T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1205" parentID="1081" level="5"
            createDate="2010-10-12T12:14:06"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="15"
            updateDate="2010-10-12T12:14:41"
            nodeName="Fusce augue" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1205" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-07-08T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1206" parentID="1081" level="5"
            createDate="2010-10-12T12:14:52"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="16"
            updateDate="2010-10-12T12:15:19"
            nodeName="pharetra et fermentum"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1206" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-08-09T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1207" parentID="1081"
            createDate="2010-10-12T12:15:31"
            level="5" writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="17"
            updateDate="2010-10-12T12:15:51"
            nodeName="Fusce augue purus" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1207" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-09-14T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1208" parentID="1081" level="5"
            createDate="2010-10-12T12:16:25"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="18"
            updateDate="2010-10-12T12:16:45"
            nodeName="Class aptent taciti" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1208" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-04T00:00:00</archiveUnder>
            </BlogPost>
            <BlogPost id="1209" parentID="1081" level="5"
            createDate="2010-10-12T12:17:01"
            writerID="0" creatorID="0" nodeType="1087"
            template="1089" sortOrder="19"
            updateDate="2010-10-12T12:17:29" nodeName="Class aptent"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1079,1081,1209" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
                <archiveUnder>2010-06-21T00:00:00</archiveUnder>
            </BlogPost>
        </Room>
        <Room id="1082" parentID="1079" level="4"
        createDate="2010-09-27T14:12:33"
        writerID="0" creatorID="0" nodeType="1077" template="0"
        sortOrder="2"
        updateDate="2010-10-07T14:43:09" nodeName="Test Blog"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1079,1082" isDoc="">
            <BlogPost id="1182" parentID="1082" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:51:19" updateDate="2010-10-08T17:51:58" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1082,1182" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1083" parentID="1079" level="4" writerID="0"
        createDate="2010-09-27T14:12:40"
        creatorID="0" nodeType="1077" template="1089" sortOrder="3"
        updateDate="2010-10-07T14:49:48" nodeName="Test Blog"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1079,1083" isDoc="">
            <BlogPost id="1183" parentID="1083" level="5" writerID="0" creatorID="0" nodeType="1087" template="1089" sortOrder="1" createDate="2010-10-08T17:52:22" updateDate="2010-10-08T17:52:39" nodeName="Test Blog" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1079,1083,1183" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
    <BlogCentre id="1080" parentID="1078" level="3" writerID="0"
    createDate="2010-09-27T14:11:55"
    creatorID="0" nodeType="1075" template="1076" sortOrder="2"
    updateDate="2010-10-07T14:43:23" nodeName="Blog Centre 2"
    writerName="Administrator" creatorName="Administrator"
    path="-1,1049,1078,1080" isDoc="">
        <Room id="1084" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="0" sortOrder="1" createDate="2010-09-27T14:12:45" updateDate="2010-10-07T14:43:17" nodeName="Room 1" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1084" isDoc="">
            <BlogPost id="1184" parentID="1084" level="5" writerID="0"
            createDate="2010-10-08T17:53:05"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:53:29" nodeName="Blog Post 3"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1080,1084,1184" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1085" parentID="1080" level="4" writerID="0"
        createDate="2010-09-27T14:12:50"
        creatorID="0" nodeType="1077" template="0" sortOrder="2"
        updateDate="2010-10-07T14:43:19" nodeName="Room 2"
        writerName="Administrator" creatorName="Administrator"
        path="-1,1049,1078,1080,1085" isDoc="">
            <BlogPost id="1185" parentID="1085" level="5" writerID="0"
            createDate="2010-10-08T17:53:51"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:54:15" nodeName="Blog Post 109"
            writerName="Administrator" creatorName="Administrator"
            path="-1,1049,1078,1080,1085,1185" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
        <Room id="1086" parentID="1080" level="4" writerID="0" creatorID="0" nodeType="1077" template="1089" sortOrder="3" createDate="2010-09-27T14:12:55" updateDate="2010-10-07T14:50:39" nodeName="Room 3" writerName="Administrator" creatorName="Administrator" path="-1,1049,1078,1080,1086" isDoc="">
            <BlogPost id="1186" parentID="1086" level="5" writerID="0"
            createDate="2010-10-08T17:54:28"
            creatorID="0" nodeType="1087" template="1089" sortOrder="1"
            updateDate="2010-10-08T17:54:51"
            nodeName="Blog Post 123" writerName="Administrator"
            creatorName="Administrator"
            path="-1,1049,1078,1080,1086,1186" isDoc="">
                <topicTitle>Lorem Ipsum</topicTitle>
            </BlogPost>
        </Room>
    </BlogCentre>
</Blog>

produces the wanted, correct result:

产生想要的,正确的结果:

<ul>
   <li>
      <h3>2010</h3>
      <ul>
         <li>October (7)</li>
         <li>September (2)</li>
         <li>August (3)</li>
         <li>July (1)</li>
         <li>June (3)</li>
         <li>May (1)</li>
         <li>April (1)</li>
         <li>March (1)</li>
         <li>February (1)</li>
         <li>January (1)</li>
      </ul>
   </li>
   <li>
      <h3>2009</h3>
      <ul>
         <li>December (1)</li>
         <li>November (1)</li>
         <li>October (1)</li>
      </ul>
   </li>
</ul>

Do note:

  1. Muenchian Grouping is used -- both for determining the different years and for determining the different months for each year.

    使用Muenchian Grouping - 用于确定不同的年份和确定每年的不同月份。

  2. The date is chosen between the createDate attribute and the archiveUnder child using the expression:

    使用表达式在createDate属性和archiveUnder子节点之间选择日期:

    concat(archiveUnder, @createDate[not(archiveUnder)])

This concatenation only picks @createDate if archiveUnder is missing or empty.

如果archiveUnder缺失或为空,则此连接仅选择@createDate。

.3. The transformation will produce the correct result even if the dates of the elements are unsorted.

0.3。即使元素的日期未排序,转换也会产生正确的结果。

#2


2  

As described by Jeni Tennison this would be a two-step method:

正如Jeni Tennison所描述的那样,这将是一个两步法:

  1. Identify the months with blog posts.
  2. 通过博客文章确定月份。

  3. Get all the posts for a particular month.
  4. 获取特定月份的所有帖子。

We can get the first part using the following XPath expression:

我们可以使用以下XPath表达式获取第一部分:

BlogPost[not (substring(@createDate, 1, 7) 
    = substring(preceding-sibling::*[1]/@createDate, 1, 7))]

Then, for part two, we need to count the posts using the count() function:

然后,对于第二部分,我们需要使用count()函数计算帖子:

BlogPost[not (substring(@createDate, 1, 7) 
    = substring(preceding-sibling::*[1]/@createDate, 1, 7))]

In our XSLT, we can use the expressions as follows:

在我们的XSLT中,我们可以使用如下表达式:

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

  <xsl:template match="Room">
    <ul>
      <xsl:for-each select="BlogPost[not (substring(@createDate, 1, 7) = substring(preceding-sibling::*[1]/@createDate, 1, 7))]">
        <xsl:sort select="@createDate" order="descending"/>
        <li>
          <xsl:value-of select="substring(@createDate, 1, 7)"/>
          <xsl:text>(</xsl:text>
          <xsl:value-of select="count(//BlogPost[substring(@createDate, 1, 7) = substring(current()/@createDate, 1, 7)]) "/>
          <xsl:text>)</xsl:text>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

</xsl:stylesheet>

The stylesheet above simply works month-based and doesn't consider multiple years. To support years, you can add an additional step to get the unique years first:

上面的样式表只是基于月份工作,不考虑多年。为了支持多年,您可以添加一个额外的步骤来获得独特的年份:

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

  <xsl:template match="Room">
    <ul>
      <xsl:for-each select="BlogPost[not (substring(@createDate, 1, 4) = substring(preceding-sibling::*[1]/@createDate, 1, 4))]">
        <xsl:sort select="@createDate" order="descending"/>
        <li>
          <xsl:value-of select="substring(@createDate, 1, 4)"/>
          <ul>
            <xsl:for-each select="//BlogPost[substring(@createDate, 1, 4) = substring(current()/@createDate, 1, 4) and not (substring(@createDate, 1, 7) = substring(preceding-sibling::*[1]/@createDate, 1, 7))]">
              <xsl:sort select="@createDate" order="descending"/>
              <li>
                <xsl:value-of select="substring(@createDate, 1, 7)"/>
                <xsl:text>(</xsl:text>
                <xsl:value-of select="count(//BlogPost[substring(@createDate, 1, 7) = substring(current()/@createDate, 1, 7)]) "/>
                <xsl:text>)</xsl:text>
              </li>
            </xsl:for-each>
          </ul>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

</xsl:stylesheet>

#3


2  

This stylesheet:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:m="month"
 exclude-result-prefixes="m">
    <xsl:param name="currentPage" select="/Blog"/>
    <xsl:key name="kBlogPostByYear" match="BlogPost"
             use="substring((@createDate|archiveUnder)[last()],1,4)"/>
    <xsl:key name="kBlogPostByYearMonth" match="BlogPost"
             use="substring((@createDate|archiveUnder)[last()],1,7)"/>
    <m:month>January</m:month>
    <m:month>February</m:month>
    <m:month>March</m:month>
    <m:month>April</m:month>
    <m:month>May</m:month>
    <m:month>June</m:month>
    <m:month>July</m:month>
    <m:month>August</m:month>
    <m:month>September</m:month>
    <m:month>October</m:month>
    <m:month>November</m:month>
    <m:month>December</m:month>
    <xsl:variable name="vMonth" select="document('')/*/m:*"/>
    <xsl:template match="/">
        <ul>
            <xsl:apply-templates
                 select="$currentPage/*/*/BlogPost
                                 [count(.|key('kBlogPostByYear',
                                              substring((@createDate|
                                                         archiveUnder)
                                                         [last()],
                                                        1,
                                                        4))[1])=1]">
                <xsl:sort select="substring((@createDate|archiveUnder)
                                            [last()],
                                            1,4)" order="descending"/>
            </xsl:apply-templates>
        </ul>
    </xsl:template>
    <xsl:template match="BlogPost">
        <xsl:variable name="vYear" select="substring((@createDate|
                                                      archiveUnder)[last()],
                                                      1,
                                                      4)"/>
        <ul>
            <h3>
                <xsl:value-of select="$vYear"/>
            </h3>
            <ul>
                <xsl:apply-templates
                     select="$currentPage/*/*/BlogPost
                                         [generate-id() =
                                          generate-id(
                                             key('kBlogPostByYearMonth',
                                                 concat($vYear,'-',
                                                        substring(
                                                           (@createDate|
                                                            archiveUnder)
                                                                 [last()],
                                                           6,
                                                           2))))]"
                                     mode="month">
                    <xsl:sort select="substring((@createDate|archiveUnder)
                                                [last()],
                                                6,2)" order="descending"/>
                </xsl:apply-templates>
            </ul>
        </ul>
    </xsl:template>
        <xsl:template match="BlogPost" mode="month">
        <xsl:variable name="vDate"
                      select="(@createDate|archiveUnder)[last()]"/>
        <li>
            <xsl:value-of select="concat($vMonth
                                            [number(
                                               substring($vDate,
                                                         6,
                                                         2))],
                                         ' (',
                                         count(key('kBlogPostByYearMonth',
                                                   substring($vDate,1,7))),
                                         ')')"/>
        </li>
    </xsl:template>
</xsl:stylesheet>

Output:

<ul>
    <ul>
        <h3>2010</h3>
        <ul>
            <li>October (7)</li>
            <li>September (2)</li>
            <li>August (3)</li>
            <li>July (1)</li>
            <li>June (3)</li>
            <li>May (1)</li>
            <li>April (1)</li>
            <li>March (1)</li>
            <li>February (1)</li>
            <li>January (1)</li>
        </ul>
    </ul>
    <ul>
        <h3>2009</h3>
        <ul>
            <li>December (1)</li>
            <li>November (1)</li>
            <li>October (1)</li>
        </ul>
    </ul>
</ul>

Edit 3: Sorry, I've missed the sorting. Now added. Also, minor refactor with a $currentPage param.

编辑3:对不起,我错过了排序。现在补充说。另外,带有$ currentPage参数的次要重构。