【问题标题】:Grouping XML elements based on attributes with XSLT使用 XSLT 基于属性对 XML 元素进行分组
【发布时间】:2012-07-03 17:37:08
【问题描述】:

我正在开发一个网页,该网页基于我无权更改的 XML 提要发布演示计划。

Feed 如下所示:

  <track name="Track 1">
    <session name="Session 1" starttime="2012-06-06 10:45" endtime="2012-06-06 12:45">
      <presentation name="Presentation 1">
        ...presentation info
      </presentation>
      <presentation name="Presentation 2">
        ...presentation info
      </presentation>
    </session>
    <session name="Session 2" starttime="2012-06-06 10:45" endtime="2012-06-06 12:45">
      <presentation name="Presentation 3">
        ...presentation info
      </presentation>
      <presentation name="Presentation 4">
        ...presentation info
      </presentation>
    </session>
    <session name="Session 3" starttime="2012-06-07 08:45" endtime="2012-06-07 10:45">
      <presentation name="Presentation 5">
        ...presentation info
      </presentation>
      <presentation name="Presentation 6">
        ...presentation info
      </presentation>
    </session>
  </track>

目前,我正在执行&lt;xsl:for-each select="session"&gt; 循环来提取信息,但结果是我输出了重复的日期和时间。

我做实际的日期和时间解析没有问题,所以我目前输出的是 2012 年 6 月 6 日; 10:45 没有问题,但是由于for-each,每次都重复,如下:

2012 年 6 月 6 日

10:45-12:45

第 1 节:演示 1,演示 2

2012 年 6 月 6 日

10:45-12:45

第 2 节:演示文稿 3,演示文稿 4

2012 年 6 月 7 日:

8:45-10:45

第 3 节:演示文稿 5、演示文稿 6

我想要以某种方式提取所有常见的日期时间,例如,获得如下输出:

2012 年 6 月 6 日:

10:45-12:45

第 1 节:演示 1、演示 2

第 2 节:演示文稿 3、演示文稿 4

2012 年 6 月 7 日:

8:45-10:45

第 3 节:演示文稿 5、演示文稿 6

作为参考,这是我当前的实现:

<xsl:for-each select="session">
  <h4>
    <!-- output to Month, DD, YYYY -->
    <xsl:call-template name="formatDate">
      <xsl:with-param name="dateTime" select="@starttime" />
    </xsl:call-template>
  </h4>

  <h5>
    <!-- output time -->
    <xsl:call-template name="formatTime">
      <xsl:with-param name="dateTime" select="@starttime" />
    </xsl:call-template> - 
    <xsl:call-template name="formatTime">
      <xsl:with-param name="dateTime" select="@endtime" />
    </xsl:call-template>
  </h5>

  <!-- session title -->
  <h5><xsl:value-of select="@name"/></h5>

  <!-- presentation title -->          
  <xsl:for-each select="presentation">
    <xsl:value-of select="@name"/><xsl:element name="br"/>
  </xsl:for-each>

</xsl:for-each>

还有日期时间格式化程序:

<!-- formatting dateTime -->
<xsl:template name="formatDate">
  <xsl:param name="dateTime" />
  <xsl:variable name="date" select="substring-before($dateTime, ' ')" />
  <xsl:variable name="year" select="substring-before($date, '-')" />
  <xsl:variable name="month" select="number(substring-before(substring-after($date, '-'), '-'))" />
  <xsl:variable name="day" select="number(substring-after(substring-after($date, '-'), '-'))" />

  <!-- output -->
  <xsl:choose>
    <xsl:when test="$month = '1'">January</xsl:when>
    <xsl:when test="$month = '2'">February</xsl:when>
    <xsl:when test="$month = '3'">March</xsl:when>
    <xsl:when test="$month = '4'">April</xsl:when>
    <xsl:when test="$month = '5'">May</xsl:when>
    <xsl:when test="$month = '6'">June</xsl:when>
    <xsl:when test="$month = '7'">July</xsl:when>
    <xsl:when test="$month = '8'">August</xsl:when>
    <xsl:when test="$month = '9'">September</xsl:when>
    <xsl:when test="$month = '10'">October</xsl:when>
    <xsl:when test="$month = '11'">November</xsl:when>
    <xsl:when test="$month = '12'">December</xsl:when>
  </xsl:choose>
  <xsl:value-of select="' '" />
  <xsl:value-of select="$day" />
  <xsl:value-of select="', '" />
  <xsl:value-of select="$year" />
</xsl:template>

<!-- formatting dateTime -->
<xsl:template name="formatTime">
  <xsl:param name="dateTime" />
  <xsl:value-of select="substring-after($dateTime, ' ')" />
</xsl:template>

【问题讨论】:

    标签: xml xslt xpath xml-parsing


    【解决方案1】:

    您想使用Muenchian method 进行分组。立即将其添加到样式表的根元素中:

    <xsl:key name="sessions-by-track-name-starttime-and-endtime" match="/track/session" use="concat(parent::track/@name, ' ', @starttime, ' ', @endtime)"/>
    

    然后如图所示更新您的 XSLT:

    <xsl:for-each select="session[generate-id() = generate-id(key('sessions-by-track-name-starttime-and-endtime', concat(parent::track/@name, ' ', @starttime, ' ', @endtime))[1])]">
      <h4>
        <!-- output to Month, DD, YYYY -->
        <xsl:call-template name="formatDate">
          <xsl:with-param name="dateTime" select="@starttime" />
        </xsl:call-template>
      </h4>
    
      <h5>
        <!-- output time -->
        <xsl:call-template name="formatTime">
          <xsl:with-param name="dateTime" select="@starttime" />
        </xsl:call-template> - 
        <xsl:call-template name="formatTime">
          <xsl:with-param name="dateTime" select="@endtime" />
        </xsl:call-template>
      </h5>
    
      <xsl:for-each select="key('sessions-by-track-name-starttime-and-endtime', concat(parent::track/@name, ' ', @starttime, ' ', @endtime))">
    
        <!-- session title -->
        <h5><xsl:value-of select="@name"/></h5>
    
        <!-- presentation title -->          
        <xsl:for-each select="presentation">
          <xsl:value-of select="@name"/><xsl:element name="br"/>
        </xsl:for-each>
    
      </xsl:for-each>
    
    </xsl:for-each>
    

    【讨论】:

      【解决方案2】:

      首先,避免在 XSLT 中使用 for-each,而是将节点应用于模板。

      试试这个(精简,没有调用call-template,因为你没有发布那个位),你可以在this XMLPlayground运行。

      <!-- sessions -->
      <xsl:template match='track/session'>
          <xsl:if test='not(preceding-sibling::session[@starttime = current()/@starttime])'>
              <h4><xsl:value-of select="@starttime" /></h4>
              <h5><xsl:value-of select="concat(@starttime,' - ',@endtime)" /></h5>
              <p><xsl:apply-templates select="presentation" /></p>
              <xsl:variable name='other_pres' select="following-sibling::session[@starttime = current()/@starttime]/presentation" />
              <xsl:if test='count($other_pres)'>
                  <p><xsl:apply-templates select="$other_pres" /></p>
              </xsl:if>
          </xsl:if>
      </xsl:template>
      
      <!-- presentations -->
      <xsl:template match='presentation'>
          <xsl:if test='position() = 1'>
              <strong><xsl:value-of select='../@name' />: </strong>
          </xsl:if>
          <xsl:value-of select="@name"/>
          <xsl:if test='position() != last()'>, </xsl:if>
      </xsl:template>
      

      这里的概念是,在session 模板中,对于每个会话,我们首先查看我们之前是否已经处理过具有相同@starttime 的会话(因为我假设这是您要负责的属性重复)。如果是这样,我们跳过它。

      然后,在输出会话的演示文稿(由它们自己的模板处理,您会注意到)时,我们还会处理当前session 的兄弟节点的任何演示文稿。

      Output:(无法访问您的日期格式模板)

      <h4>2012-06-06 10:45</h4>
      <h5>2012-06-06 10:45 - 2012-06-06 12:45</h5>
      <p><strong>Session 1: </strong>Presentation 1, Presentation 2</p><p><strong>Session 2: </strong>Presentation 3, Presentation 4</p>
      
      <h4>2012-06-07 08:45</h4>
      <h5>2012-06-07 08:45 - 2012-06-07 10:45</h5>
      <p><strong>Session 3: </strong>Presentation 5, Presentation 6</p>
      

      【讨论】:

      • 我知道你在那里做了什么,但另一种方法涉及的代码重构更少……为你的时间点赞:)
      • 是的,Muenchian 方法是另一种明显的方法。我选择了一个不使用密钥的 :) Ta 无论如何都要投票,很高兴你得到了它的排序。
      • 很抱歉挖掘了一个旧答案,但我会和你一起去。一个问题...假设我创建了一个变量来替换您的 if-test ($cdate) 中的 @starttime ...有没有办法进一步应用它?基本上,尝试从我拥有的日期时间字段中删除 dd/mm/yy,然后对其进行排序。
      猜你喜欢
      • 1970-01-01
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多