如何使用组相邻(XSLT)重新格式化XML

1 xml xslt transformation

我是这个XSLT的新手,我无法弄清楚如何:

这是xml的片段,我从以下开始:

<Article>    
<Bullettext>10,00 </Bullettext>  
<Bullettext>8,00 </Bullettext>    
</Article>  
<Article>  
<something>some text</something>  
</Article>  
<Article>  
<Corpsdetexte>Bulgaria</Corpsdetexte>  
<Bullettext>15,0 </Bullettext>  
<Bullettext>10,0 </Bullettext>  
</Article> ` 
Run Code Online (Sandbox Code Playgroud)

这就是我想要的输出:

<LIST>  
<ITEM>12,00 </ITEM>  
<ITEM>10,00 </ITEM>  
<ITEM>8,00 </ITEM>  
</LIST>  
<P>  
<something>some text</something>  
</P>  

<P>  
<Corpsdetexte>Bulgaria</Corpsdetexte>  
</P>  
<LIST>  
<ITEM>15,0 </ITEM>  
<ITEM>10,0 </ITEM>  
</LIST>  
Run Code Online (Sandbox Code Playgroud)

有任何想法吗??

Rob*_*ney 5

根据您对Rubens Farias的回答的评论(实际上,您应该编辑要包含的问题),似乎您需要一种通用方法将任何相邻BulletText元素组转换为列表.这让我们得到两个问题:我们如何找到这样的群体,并找到它们,我们如何将它们转换为列表?

为了找到一个群体,我们需要寻找所有BulletText其前一个兄弟元素是不是一个BulletText元素.其中每一个都开始一个组,这些是我们要转换成列表的元素.所以我们要做的第一件事就是创建一个可以找到它们的XPath表达式:

BulletText[not(preceding-sibling::*[1][name()='BulletText'])]
Run Code Online (Sandbox Code Playgroud)

如果你看一下XPath表达式中的谓词,那就是我所说的我们需要做的事情:它匹配一个BulletText元素,如果不是它的第一个前面的兄弟(preceding-sibling::*[1])的名字是BulletText.请注意,如果该元素没有前面的兄弟,这个表达式会匹配.

所以现在我们可以创建一个匹配这些start-of-group元素的模板.我们在这个模板中放了什么?我们将把这些元素转换为LIST元素,因此模板开始看起来像:

<LIST>
   ...
</LIST>
Run Code Online (Sandbox Code Playgroud)

很容易.但是,我们如何找到将填充该列表的元素?我们有两种情况需要处理.

第一个很简单:如果以下所有兄弟BulletText元素都是元素,我们希望使用此元素及其所有后续兄弟元素填充列表.

第二个更难.如果有一个不是BulletText元素的跟随兄弟,我们希望我们的列表是当前元素的父元素的所有子元素,从当前元素开始并在stop元素之前结束.下面是我们需要使用count()函数计算起始和结束索引的position()函数,以及查找每个元素位置的函数.

完成的模板如下所示:

<xsl:template match="BulletText[not(preceding-sibling::*[1][name()='BulletText'])]">
  <!-- find the element that we want to stop at -->
  <xsl:variable name="stop" select="./following-sibling::*[name() != 'BulletText'][1]"/>
  <LIST>
    <xsl:choose>
      <!-- first, the simple case:  there's no element we have to stop at -->
      <xsl:when test="not($stop)">
        <xsl:apply-templates select="." mode="item"/>
        <xsl:apply-templates select="./following-sibling::BulletText" mode="item"/>
      </xsl:when>
      <!-- transform all elements between the start and stop index into items -->
      <xsl:otherwise>
        <xsl:variable name="start_index" select="count(preceding-sibling::*) + 1"/>
        <xsl:variable name="stop_index" select="count($stop/preceding-sibling::*)"/>
        <xsl:apply-templates select="../*[position() &gt;= $start_index 
                                      and position() &lt;= $stop_index]"
                             mode="item"/>
      </xsl:otherwise>
    </xsl:choose>
  </LIST>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

您还需要两个其他模板.一个将BulletText元素转换为项目 - 我们mode在这里使用,以便我们可以将它应用于BulletText元素而无需调用我们当前使用的模板:

<xsl:template match="BulletText" mode="item">
   <ITEM>
      <xsl:value-of select="."/>
   </ITEM>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

然后你还需要一个模板来保持BulletText我们的第一个模板匹配的元素生成任何输出(因为如果我们使用身份变换,如果我们不这样做,它们就会被复制):

<xsl:template match='BulletText'/>
Run Code Online (Sandbox Code Playgroud)

由于XSLT的模板优先级规则的神奇之处BulletText,两个模板匹配的任何元素都将被第一个转换,而这一个将捕获其余的元素.

只需将这三个模板添加到身份转换中,就可以了.