使用XSLT

时间:2018-10-11 10:50:27

标签: xml xslt xpath

我具有以下XML结构:

<node0>
  <beforeContainer @id="test">
   <beforeContainer2/>
  </beforeContainer> 
    <container id='1'>
      <node1>
        <node2>
          <text>Test1</text>
        </node2>
      </node1>
    </container>
    <node3>
      <node4>
        <text>Test2</text>
        <node5>
          <container id='2'>
            <node6>
              <button>Click</button>
            </node6>
          </container>
        </node5>
      </node4>
    </node3>
    <text>Test3</text>
     <node7>
       <node8>
         <node9>
           <container id='3'>
             <node10>
               <button>Click2</button>
             </node10>
           </container>
         </node9>
       </node8>
     </node7>
   </node0>

我必须提取后续<container>个节点之间的所有节点名称。 <container>节点在树中的深度级别不同。

输出应如下所示:

<container id='1'>
  <node1/>
  <node2/>
  <text/>
  <node3/>
  <node4/>
  <text/>
  <node5/>
</container>
<container id='2'>
  <node6/>
  <button/>
  <text/>
  <node7/>
  <node8/>
  <node9/>
</container>
<container id='3'>
  <node10/><button/>
</container>

我尝试了不同的x-Path表达式,但未成功,因为它们始终具有特定的范围(后代或自身:: node(),后继兄弟等)。

1 个答案:

答案 0 :(得分:2)

XSLT 2或3中的简单分组问题

  <xsl:template match="/*">
      <xsl:for-each-group select="descendant::*" group-starting-with="container">
          <xsl:copy>
              <xsl:copy-of select="@*"/>
              <xsl:apply-templates select="current-group() except ." mode="copy"/>
          </xsl:copy>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="*" mode="copy">
      <xsl:copy/>
  </xsl:template>

完整示例https://xsltfiddle.liberty-development.net/bdxtqQ是:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/*">
      <xsl:for-each-group select="descendant::*" group-starting-with="container">
          <xsl:copy>
              <xsl:copy-of select="@*"/>
              <xsl:apply-templates select="current-group() except ." mode="copy"/>
          </xsl:copy>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="*" mode="copy">
      <xsl:copy/>
  </xsl:template>

</xsl:stylesheet>

关于您不想在第一个container之前包含元素的编辑,请将模板进行分组更改为

  <xsl:template match="/*">
      <xsl:for-each-group select="descendant::*" group-starting-with="container">
          <xsl:if test="self::container">
              <xsl:copy>
                  <xsl:copy-of select="@*"/>
                  <xsl:apply-templates select="current-group() except ." mode="copy"/>
              </xsl:copy>              
          </xsl:if>
      </xsl:for-each-group>
  </xsl:template>

https://xsltfiddle.liberty-development.net/bdxtqQ/1

for-each-group group-starting-with的工作方式(请参阅https://www.w3.org/TR/xslt-30/#element-for-each-group)是,如果组填充中的第一项与group-starting-with模式不匹配,则仍会形成一个组:

  

如果存在group-starting-with属性,则其值必须   成为模式。

     

按人口顺序检查人口中的项目。如果   项与模式匹配,或者是总体中的第一项,然后   创建一个新组,该项目成为其第一个成员。   否则,该项目将与其之前的项目附加到同一组   人口中的项目。

在许多情况下这很有用,但随后通常需要在xsl:if内使用嵌套的xsl:choosexsl:for-each-group来测试我们是否有与{{1 }}模式,或在第一个此类组之前收集了任何物品。