2

关于 Umbraco XSLT 版本 1。

我有大约。XML 格式的 150 个新闻项目。可以这样说(在我更熟悉这个 xml/xslt 之前,一切都是伪代码):

<news>
  <data alias=date>2008-10-20</data>
</news>
<news>
  <data alias=date>2009-11-25</data>
</news><news>
  <data alias=date>2009-11-20</data>
</news> etc. etc....

我想通过 XML 运行并创建 html 输出作为新闻存档。类似的东西(标签不重要):

2008
  Jan
  Feb
  ...
2009
  Jan
  Feb
  Mar
  etc. etc.

我只能想出一个嵌套的for-each(伪代码):

var year_counter = 2002
var month_counter = 1
<xsl:for-each select="./data [@alias = 'date']=year_counter">
  <xsl:for-each select="./data [@alias = 'date']=month_counter">
    <xsl:value-of select="data [@alias = 'date']>
  "...if month_counter==12 end, else month_counter++ ..."
  </xsl:for-each>
"... year_counter ++ ..."
</xsl:for-each>

但是一位程序员指出,循环 10 年将产生 120 次循环,这是糟糕的编码。因为我认为 Umbraco 缓存了结果,所以我不太担心,而且在这种情况下会有一个最大值。150 条记录。

关于如何排序和输出许多新闻项目并将它们按年分组并将每年按月分组的任何线索?

兄弟。安德斯

4

4 回答 4

4

对于以下解决方案,我使用了这个 XML 文件:

<root>
  <news>
    <data alias="date">2008-10-20</data>
  </news>
  <news>
    <data alias="date">2009-11-25</data>
  </news>
  <news>
    <data alias="date">2009-11-20</data>
  </news>
  <news>
    <data alias="date">2009-03-20</data>
  </news>
  <news>
    <data alias="date">2008-01-20</data>
  </news>
</root>

和这个 XSLT 1.0 转换:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:cfg="http://tempuri.org/config"
  exclude-result-prefixes="cfg"
>
  <xsl:output method="xml" encoding="utf-8" />

  <!-- index news by their "yyyy" value (first 4 chars) -->
  <xsl:key 
    name="kNewsByY"  
    match="news" 
    use="substring(data[@alias='date'], 1, 4)" 
  />
  <!-- index news by their "yyyy-mm" value (first 7 chars) -->
  <xsl:key 
    name="kNewsByYM" 
    match="news" 
    use="substring(data[@alias='date'], 1, 7)" 
  />

  <!-- translation table (month number to name) -->
  <config xmlns="http://tempuri.org/config">
    <months>
      <month id="01" name="Jan" />
      <month id="02" name="Feb" />
      <month id="03" name="Mar" />
      <month id="04" name="Apr" />
      <month id="05" name="May" />
      <month id="06" name="Jun" />
      <month id="07" name="Jul" />
      <month id="08" name="Aug" />
      <month id="09" name="Sep" />
      <month id="10" name="Oct" />
      <month id="11" name="Nov" />
      <month id="12" name="Dec" />
    </months>
  </config>

  <xsl:template match="root">
    <xsl:copy>
      <!-- group news by "yyyy" -->
      <xsl:apply-templates mode="year" select="
        news[
          generate-id()
          =
          generate-id(key('kNewsByY', substring(data[@alias='date'], 1, 4))[1])
        ]
      ">
        <xsl:sort select="data[@alias='date']" order="descending" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!-- year groups will be enclosed in a <year> element -->
  <xsl:template match="news" mode="year">
    <xsl:variable name="y" select="substring(data[@alias='date'], 1, 4)" />
    <year num="{$y}">
      <!-- group this year's news by "yyyy-mm" -->
      <xsl:apply-templates mode="month" select="
        key('kNewsByY', $y)[
          generate-id() 
          =
          generate-id(key('kNewsByYM', substring(data[@alias='date'], 1, 7))[1])
        ]
      ">
        <xsl:sort select="data[@alias='date']" order="descending" />
      </xsl:apply-templates>
    </year>
  </xsl:template>

  <!-- month groups will be enclosed in a <month> element -->
  <xsl:template match="news" mode="month">
    <xsl:variable name="ym" select="substring(data[@alias='date'], 1, 7)" />
    <xsl:variable name="m" select="substring-after($ym, '-')" />
    <!-- select the label of the current month from the config -->
    <xsl:variable name="label" select="document('')/*/cfg:config/cfg:months/cfg:month[@id = $m]/@name" />
    <month num="{$m}" label="{$label}">
      <!-- process news of the current "yyyy-mm" group -->
      <xsl:apply-templates select="key('kNewsByYM', $ym)">
        <xsl:sort select="data[@alias='date']" order="descending" />
      </xsl:apply-templates>
    </month>
  </xsl:template>

  <!-- for the sake of this example, news elements will just be copied -->
  <xsl:template match="news">
    <xsl:copy-of select="." />
  </xsl:template>
</xsl:stylesheet>

应用转换后,将产生以下输出:

<root>
  <year num="2009">
    <month num="11" label="Nov">
      <news>
        <data alias="date">2009-11-25</data>
      </news>
      <news>
        <data alias="date">2009-11-20</data>
      </news>
    </month>
    <month num="03" label="Mar">
      <news>
        <data alias="date">2009-03-20</data>
      </news>
    </month>
  </year>
  <year num="2008">
    <month num="10" label="Oct">
      <news>
        <data alias="date">2008-10-20</data>
      </news>
    </month>
    <month num="01" label="Jan">
      <news>
        <data alias="date">2008-01-20</data>
      </news>
    </month>
  </year>
</root>

它已经具有正确的结构,您可以根据自己的需要调整实际外观。

解决方案是两阶段 Muenchian 分组方法。在第一阶段,新闻项目按年分组,在第二阶段按年月分组。

请参考我对这里<xsl:key>的解释。您不需要阅读其他问题,尽管这是一个类似的问题。只需阅读我答案的下半部分。key()

于 2009-11-20T14:57:15.967 回答
2

您需要的是所谓的Muenchian Grouping方法,它正好解决了 XSLT 的这个问题/模式。

基本上,它通过查找唯一键并循环遍历正在使用的键中包含的条目来进行分组。

于 2009-11-20T13:27:39.123 回答
0

除了 lucero,查看Xsl 分组重复问题以避免月份名称被删除的问题

于 2009-11-20T13:34:24.573 回答
0

您不能在 XSLT 中使用 month_counter++,它不是一种过程语言,也不是 XSLT 的工作方式。因此,如果这不起作用,那么担心效率低下是没有意义的。

这看起来像是 XSLT 中的一大难题。我的 XSLT 还不够新鲜,无法尝试并实际实现它。但这里有两种方法:

1)

  • 使用 xsl:key 提取所有唯一年份-
  • 然后迭代这些年。每年做
  • 使用 xsl:key 提取所有月份
  • 每个月做

2)(如果可行的话,似乎更容易。)

  • 按日期对它们进行排序,将排序后的数组保存在变量中
  • 迭代这个变量(变量保存排序数组很重要)
  • 每次查看前兄弟。如果其年/月不等于当前元素,则写入相应的标题

3) 忘记 XSLT,使用真正的编程语言。

于 2009-11-20T13:36:01.357 回答