合并相同类型的相邻节点(XSLT 1.0)

时间:2009-11-26 23:33:52

标签: xslt

是否可以合并相同指定类型的每个节点序列? (在这种情况下为'aaa')(不仅仅是第一次出现的序列)

这是我的XML输入:

<block>
    <aaa>text1</aaa>
    <aaa>text2</aaa>
    <aaa><xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8</aaa>
    <aaa><zzz>text9</zzz></aaa>
    <aaa>texta</aaa>
</block>

我想要关注输出:

<block>
    <aaa>text1text2<xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8<zzz>text9</zzz>texta</aaa>
</block>

任何帮助表示赞赏

3 个答案:

答案 0 :(得分:0)

假设您只有一个blockMuenchian method是最优化的方式:

<!-- group nodes by name -->
<xsl:key name="block-children-by-name" match="block/*" use="name()"/>

<!-- for nodes that aren't first in their group, no output -->
<xsl:template match="block/*" />

<!-- for nodes that are first in their group, combine group children and output -->
<xsl:template match="block/*[generate-id() =
                             generate-id(key('block-children-by-name', name())[1])]">
   <xsl:copy>
     <xsl:copy-of select="key('block-children-by-name', name())/*"/>
   </xsl:copy>
</xsl:template>

请注意,这仅合并子节点,而不是例如aaabbb本身可能出现的任何属性。

答案 1 :(得分:0)

这是另一种方法。

首先,匹配块元素的所有子节点

<xsl:template match="block/child::*">

接下来,检查元素的最直接兄弟是否有不同的名称,表明这是一个或多个相邻元素中的第一个:

<xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">

如果是,您可以复制该节点。然后,您需要复制具有相同名称的以下兄弟姐妹。我这样做是通过递归调用每个紧跟在兄弟之后使用相同名称

的模板
<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>

将所有这些放在一起给出了

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

   <!-- Match children of the block element -->
   <xsl:template match="block/child::*">
      <xsl:variable name="name" select="local-name()"/>

      <!-- Is this the first element in a sequence? -->
      <xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">
         <xsl:copy>
            <xsl:apply-templates />

            <!-- Match the next sibling if it has the same name -->
            <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <!-- Recursive template used to match the next sibling if it has the same name -->
   <xsl:template match="block/child::*" mode="next">
      <xsl:variable name="name" select="local-name()"/>
         <xsl:apply-templates />
      <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
   </xsl:template>

   <!-- Template used to copy a generic node -->
   <xsl:template match="@* | node()">
         <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
         </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

答案 2 :(得分:0)

这是另一种方法,不使用递归模板。

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

  <xsl:template match="aaa">
    <xsl:if test="not(preceding-sibling::*[1]/self::aaa)">
      <xsl:variable name="following" 
                    select="following-sibling::aaa[
                              not(preceding-sibling::*[
                                not(self::aaa) and
                                not(following-sibling::aaa = current())
                              ])
                            ]"/>
      <xsl:copy>
        <xsl:apply-templates select="$following/@*"/>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="node()"/>
        <xsl:apply-templates select="$following/node()"/>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

用于选择与当前节点合并的以下兄弟aaa节点的相当复杂的XPath表达式:

following-sibling::aaa[                       # following 'aaa' siblings
  not(preceding-sibling::*[                   #   if they are not preceded by
    not(self::aaa) and                        #     a non-'aaa' node
    not(following-sibling::aaa = current())   #     after the current node
  ])
]