如何使用组相邻 (XSLT) 重新格式化 XML [英] How to reformat XML with group-adjacent (XSLT)

查看:40
本文介绍了如何使用组相邻 (XSLT) 重新格式化 XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是这个 XSLT 方面的新手,我不知道如何做到这一点:

I'm new in this XSLT-thing and I can't figure out how to this:

这是我开头的 xml 的一个片段:

This is a snippet from the xml i start with:

<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> ` 

这就是我想要的输出:

<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>  

有什么想法吗??

推荐答案

从您对 Rubens Farias 的回答的评论(实际上,您应该编辑您的问题以包括在内),似乎您想要一种通用的方式将任意一组相邻的 BulletText 元素转换为一个列表.这让我们面临两个问题:我们如何找到这样的组,找到它们后,我们如何将它们转换为列表?

From your comment in response to Rubens Farias's answer (and really, that's something you should edit your question to include), it seems that you want a generic way to transform any group of adjacent BulletText elements into a list. That gets us to two questions: how do we find such groups, and having found them, how do we transform them into a list?

要找到一个组,我们需要查找所有 BulletText 元素,其紧邻的同级 不是 一个 BulletText 元素.每一个都开始一个组,这些是我们要转换成列表的元素.所以我们要做的第一件事是创建一个 XPath 表达式来找到它们:

To find a group, we need to look for all BulletText elements whose immediately preceding sibling isn't a BulletText element. Each one of those starts a group, and those are the elements that we're going to transform into lists. So the first thing we want to do is create an XPath expression that will find them:

BulletText[not(preceding-sibling::*[1][name()='BulletText'])]

如果您查看该 XPath 表达式中的谓词,它正在执行我所说的我们需要做的事情:如果不是它的第一个前面的兄弟元素(preceding-sibling::*[1]) 的名称为 BulletText.请注意,如果元素 没有 之前的兄弟元素,则此表达式仍将匹配它.

If you look at the predicates in that XPath expression, it's doing just what I said we need to do: it matches a BulletText element if it's not the case that its first preceding sibling (preceding-sibling::*[1]) has a name of BulletText. Note that if the element has no preceding sibling, this expression will still match it.

所以现在我们可以创建一个匹配这些组开始元素的模板.我们在这个模板里放了什么?我们将把这些元素转换成 LIST 元素,所以模板开始看起来像:

So now we can create a template that matches these start-of-group elements. What do we put inside this template? We're going to transform these elements into LIST elements, so the template's going to start out looking like:

<LIST>
   ...
</LIST>

很简单.但是我们如何找到将要填充该列表的元素呢?我们必须处理两种情况.

Easy enough. But how do we find the elements that are going to populate that list? There are two cases we have to deal with.

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

The first is simple: if all of the following siblings are BulletText elements, we want to populate the list with this element and all of its following siblings.

第二个更难.如果后面的兄弟元素不是 BulletText 元素,我们希望我们的列表是当前元素父元素的所有子元素,从当前元素开始到停止元素之前结束.这里有一个例子,我们需要使用 count() 函数来计算开始和结束索引,并使用 position() 函数来找到每个元素的位置.

The second is harder. If there's a following sibling that's not a BulletText element, we want our list to be all of the children of the current element's parent, starting at the current element and ending before the stop element. Here is a case where we need to use the count() function to calculate the starting and ending indexes, and the position() function to find the position of each element.

完成的模板如下所示:

<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>

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

You need two other templates. One converts BulletText elements into items - we use mode here so that we can apply it to BulletText elements without invoking the template we're currently using:

<xsl:template match="BulletText" mode="item">
   <ITEM>
      <xsl:value-of select="."/>
   </ITEM>
</xsl:template>

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

Then you also need a template that keeps BulletText elements that our first template doesn't match from generating any output (because if we're using the identity transform, they'll just get copied if we don't):

<xsl:template match='BulletText'/>

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

Because of the magic of XSLT's template precedence rules, any BulletText element that both templates match will be transformed by the first one, and this one will catch the rest.

只需将这三个模板添加到身份转换中即可.

Just add those three templates to the identity transform and you're good to go.

这篇关于如何使用组相邻 (XSLT) 重新格式化 XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆