如何使用XSLT1按标签折叠一组选定的(相邻)标签?

我有一组必须包含在新元素中的顺序节点.例:

  <root>
    <c>cccc</c>
    <a gr="g1">aaaa</a>    <b gr="g1">1111</b>
    <a gr="g2">bbbb</a>   <b gr="g2">2222</b>
  </root>

必须由折叠标签括起来,导致(在XSLT之后):

  <root>
    <c>cccc</c>
    <fold><a gr="g1">aaaa</a>    <b gr="g1">1111</b></fold>
    <fold><a gr="g2">bbbb</a>   <b gr="g2">2222</b></fold>
  </root>

因此,我有一个“用于分组的标签”(@gr),但无法想象如何产生正确的折叠标签.

我正在尝试使用this questionthis other one的线索…但是我有一个“分组标签”,因此我知道我的解决方案不需要使用key()函数.

我的非一般解决方案是:

   <xsl:template match="/">
       <root>
       <xsl:copy-of select="root/c"/>
       <fold><xsl:for-each select="//*[@gr='g1']">
             <xsl:copy-of select="."/>
       </xsl:for-each></fold>

       <fold><xsl:for-each select="//*[@gr='g2']">
             <xsl:copy-of select="."/>
       </xsl:for-each></fold>

       </root>
   </xsl:template>

我需要一个通用的解决方案(!),通过所有@gr循环并应对(标识)所有没有@gr的上下文…也许使用标识转换.

另一个(未来)的问题是通过折叠的方式递归执行此操作.

最佳答案
在XSLT 1.0中,处理此类问题的标准技术称为Muenchian分组,它涉及使用定义节点应如何分组的键以及使用generate-id仅提取每个组中的第一个节点作为技巧的技巧.代表整个小组.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:strip-space elements="*" />
  <xsl:output indent="yes" />
  <xsl:key name="elementsByGr" match="*[@gr]" use="@gr" />

  <xsl:template match="@*|node()" name="identity">
    <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>

  <!-- match the first element with each @gr value -->
  <xsl:template match="*[@gr][generate-id() =
         generate-id(key('elementsByGr', @gr)[1])]" priority="2">
    <fold>
      <xsl:for-each select="key('elementsByGr', @gr)">
        <xsl:call-template name="identity" />
      </xsl:for-each>
    </fold>
  </xsl:template>

  <!-- ignore subsequent ones in template matching, they're handled within
       the first element template -->
  <xsl:template match="*[@gr]" priority="1" />
</xsl:stylesheet>

这可以实现您想要的分组,但是就像您的非一般性解决方案一样,它不会保留缩进和a和b元素之间的空格文本节点,即它将为您提供

<root>
  <c>cccc</c>
  <fold>
    <a gr="g1">aaaa</a>
    <b gr="g1">1111</b>
  </fold>
  <fold>
    <a gr="g2">bbbb</a>
    <b gr="g2">2222</b>
  </fold>
</root>

请注意,如果您能够使用XSLT 2.0,则整个过程将成为一个for-each-group:

<xsl:template match="root">
  <xsl:for-each-group select="*" group-adjacent="@gr">
    <xsl:choose>
      <!-- wrap each group in a fold -->
      <xsl:when test="@gr">
        <fold><xsl:copy-of select="current-group()" /></fold>
      </xsl:when>
      <!-- or just copy as-is for elements that don't have a @gr -->
      <xsl:otherwise>
        <xsl:copy-of select="current-group()" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:template>

转载注明原文:如何使用XSLT1按标签折叠一组选定的(相邻)标签? - 代码日志