XSLT 1.0将特定的后代/子注释重新排列到父母/祖先的同一级别

时间:2011-08-11 20:18:34

标签: xml xslt

我正在寻找将一些特定后代重新排列到其祖先节点级别的一般方法。

重要的是:

  • 我需要一个适用于px的常规模板。
  • 节点a b DEEPSPACENODE的顺序(从上到下)不应该尽可能地改变。

我的意见:

<root>
    <p>
        <a>1</a>
        <a>2</a>
        <a><DEEPSPACENODE/></a>
        <b>3</b>
        <b>4</b>
        <a>5</a>
        <a>6</a>
        <b>7</b>
    </p>

    <x>
        <a>8</a>
        <a>9</a>
        <b>10</b>
        <b>11</b>
        <a>12</a>
        <a>13</a>
        <b>14</b>
    </x>    
</root>

我想要的输出:

<root>
    <p>
        <a>1</a>
        <a>2</a>
        <a/>
    </p>
    <DEEPSPACENODE/>
    <b>3</b>
    <b>4</b>
    <p>
        <a>5</a>
        <a>6</a>
    </p>
    <b>7</b>

    <x>
        <a>8</a>
        <a>9</a>
    </x>
    <b>10</b>
    <b>11</b>
    <x>
        <a>12</a>
        <a>13</a>
    </x>
    <b>14</b>
</root>

感谢您的帮助。我试图自己解决,但我没有成功。

2 个答案:

答案 0 :(得分:2)

检查此样式表:

  • 通过检查前面的兄弟
  • 来分组 a 元素
  • 使用相邻的递归模板聚合 a

注意我故意没有管理DEEPSPACENODE,因为不清楚你想如何处理它:P。您可以将此转换用作起点。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="text()"/>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="*/*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="a[preceding-sibling::*[1][self::b]
        or not(preceding-sibling::*)]">
        <xsl:element name="{name(..)}">
            <xsl:apply-templates select="." mode="adjacent"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="b">
        <xsl:copy-of select="."/>
    </xsl:template>

    <xsl:template match="a" mode="adjacent">
        <a>
            <xsl:value-of select="self::node()[not(DEEPSPACENODE)]"/>
        </a>
        <xsl:apply-templates select="
            following-sibling::*[1][self::a]" mode="adjacent"/>
    </xsl:template>

</xsl:stylesheet>

带输出:

<root>
   <p>
      <a>1</a>
      <a>2</a>
      <a/>
   </p>
   <b>3</b>
   <b>4</b>
   <p>
      <a>5</a>
      <a>6</a>
   </p>
   <b>7</b>
   <x>
      <a>8</a>
      <a>9</a>
   </x>
   <b>10</b>
   <b>11</b>
   <x>
      <a>12</a>
      <a>13</a>
   </x>
   <b>14</b>
</root>

答案 1 :(得分:2)

这是一个更通用的解决方案,可以产生想要的结果而不对XML文档施加任何限制 - 我们不要假设任何预定义的嵌套级别,或者说名为b的元素存在:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="kFollowing"
   match="a[preceding-sibling::*[1]
                              [self::a]
           ]"
   use="concat(
         generate-id(
                preceding-sibling::*[not(self::a)][1]
                    ),
         generate-id(..)
               )
         "/>

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

 <xsl:template match="*[a]">
   <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match=
   "a[not(preceding-sibling::*[1][self::a])]">

   <xsl:variable name="vGroup" select=
   ".|key('kFollowing',
             concat(generate-id(preceding-sibling::*[1]),
                    generate-id(..)
                   )
          )"/>

   <xsl:element name="{name(..)}">
     <xsl:apply-templates mode="group"
     select="$vGroup"/>
   </xsl:element>

   <xsl:apply-templates select="$vGroup/*"/>
 </xsl:template>

 <xsl:template match="a" mode="group">
  <a>
   <xsl:apply-templates select="node()[not(self::*)]"/>
  </a>
 </xsl:template>

 <xsl:template match=
  "a[preceding-sibling::*[1]
                       [self::a]
    ]"/>
</xsl:stylesheet>

应用于提供的XML文档

<root>
    <p>
        <a>1</a>
        <a>2</a>
        <a>
            <DEEPSPACENODE/>
        </a>
        <b>3</b>
        <b>4</b>
        <a>5</a>
        <a>6</a>
        <b>7</b>
    </p>
    <x>
        <a>8</a>
        <a>9</a>
        <b>10</b>
        <b>11</b>
        <a>12</a>
        <a>13</a>
        <b>14</b>
    </x>
</root>

产生了想要的正确结果

<root>
   <p>
      <a>1</a>
      <a>2</a>
      <a/>
   </p>
   <DEEPSPACENODE/>
   <b>3</b>
   <b>4</b>
   <p>
      <a>5</a>
      <a>6</a>
   </p>
   <b>7</b>
   <x>
      <a>8</a>
      <a>9</a>
   </x>
   <b>10</b>
   <b>11</b>
   <x>
      <a>12</a>
      <a>13</a>
   </x>
   <b>14</b>
</root>

现在,使用以下XML文档,此解决方案仍然可以生成所需的结果,而@empo的解决方案会扼杀它

<root>
   <c>
    <p>
        <a>1</a>
        <a>2</a>
        <a>
            <DEEPSPACENODE/>
        </a>
        <z>3</z>
        <z>4</z>
        <a>5</a>
        <a>6</a>
        <b>7</b>
    </p>
    <x>
        <a>8</a>
        <a>9</a>
        <b>10</b>
        <z>11</z>
        <a>12</a>
        <a>13</a>
        <b>14</b>
    </x>
  </c>  
</root>

同样的转换,当应用于上面的XML文档时,再次产生正确的,想要的结果

<root>
   <c>
      <p>
         <a>1</a>
         <a>2</a>
         <a/>
      </p>
      <DEEPSPACENODE/>
      <z>3</z>
      <z>4</z>
      <p>
         <a>5</a>
         <a>6</a>
      </p>
      <b>7</b>
      <x>
         <a>8</a>
         <a>9</a>
      </x>
      <b>10</b>
      <z>11</z>
      <x>
         <a>12</a>
         <a>13</a>
      </x>
      <b>14</b>
   </c>
</root>

<强> II。 XSLT 2.0解决方案:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="*[a]">
  <xsl:for-each-group select="node()"
                      group-adjacent="name() eq 'a'">
    <xsl:apply-templates select="current-group()"
                         mode="group">
      <xsl:with-param name="pGroup"
                      select="current-group()"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="current-group()[self::a]/*"/>
   </xsl:for-each-group>
  </xsl:template>

 <xsl:template match="a" mode="group"/>

 <xsl:template mode="group"
          match="a[not(preceding-sibling::*[1][self::a])]" >
   <xsl:param name="pGroup" as="node()*"/>

   <xsl:element name="{name(..)}">
     <xsl:apply-templates select="$pGroup" mode="shallow"/>
   </xsl:element>
 </xsl:template>

  <xsl:template match="a" mode="shallow">
    <a>
      <xsl:apply-templates select="node()[not(self::*)]"/>
    </a>
   </xsl:template>
</xsl:stylesheet>