我是这个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> `
这就是我想要的输出:
<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>
任何想法??
答案 0 :(得分:5)
根据您对Rubens Farias的回答的评论(实际上,您应该编辑要包含的问题),似乎您需要一种通用的方法将任何一组相邻的BulletText
元素转换为一个列表。这让我们得到两个问题:我们如何找到这些群体,并找到它们,我们如何将它们转换为列表?
要查找组,我们需要查找其前一个兄弟不是 BulletText
元素的所有BulletText
个元素。其中每一个都开始一个组,这些是我们要转换成列表的元素。所以我们要做的第一件事就是创建一个可以找到它们的XPath表达式:
BulletText[not(preceding-sibling::*[1][name()='BulletText'])]
如果你看一下XPath表达式中的谓词,那就是我所说的我们需要做的事情:它匹配一个BulletText
元素,如果不是它的第一个前一个兄弟(preceding-sibling::*[1]
)名称为BulletText
。请注意,如果元素 没有先前的兄弟,则此表达式仍将匹配它。
现在我们可以创建一个匹配这些start-of-group元素的模板。我们在这个模板中放了什么?我们要将这些元素转换为LIST
元素,因此模板开始看起来像:
<LIST>
...
</LIST>
够容易。但是,我们如何找到将填充该列表的元素?我们有两种情况需要处理。
第一个很简单:如果以下所有兄弟节点都是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() >= $start_index
and position() <= $stop_index]"
mode="item"/>
</xsl:otherwise>
</xsl:choose>
</LIST>
</xsl:template>
您还需要两个其他模板。一个将BulletText
个元素转换为项目 - 我们在此处使用mode
,以便我们可以将其应用于BulletText
元素,而无需调用我们当前使用的模板:
<xsl:template match="BulletText" mode="item">
<ITEM>
<xsl:value-of select="."/>
</ITEM>
</xsl:template>
然后你还需要一个模板来保存我们的第一个模板不匹配的BulletText
个元素来生成任何输出(因为如果我们使用身份变换,他们会如果我们不这样做就被复制):
<xsl:template match='BulletText'/>
由于XSLT模板优先级规则的神奇之处,两个模板匹配的任何BulletText
元素都将被第一个元素转换,而这个元素将会捕获其余元素。
只需将这三个模板添加到身份转换中,就可以了。
答案 1 :(得分:1)
我认为您正在寻找conditional deep-copy。
以下是根据您的情况重写的上述链接中的代码:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- nodes with Bullettext children -->
<xsl:template match="*[Bullettext]">
<!-- for every child -->
<xsl:copy>
<xsl:for-each select="*">
<!-- if child is a Bullettext and it has a Bullettext before it, don't copy it (it has already been copied) -->
<xsl:if test="not(local-name(.) = 'Bullettext' and local-name(./preceding-sibling::*[1]) = 'Bullettext')">
<xsl:choose>
<xsl:when test="local-name(.) = 'Bullettext'">
<!-- copy all Bullettext children adjacent to this one and each other -->
<LIST>
<xsl:call-template name="get-all-adjacent-siblings">
<xsl:with-param name="sibling-before" select="." />
</xsl:call-template>
</LIST>
</xsl:when>
<xsl:otherwise>
<!-- copy non-Bullettext child -->
<xsl:apply-templates select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template name="get-all-adjacent-siblings">
<xsl:param name="sibling-before" />
<!-- return me -->
<xsl:copy>
<xsl:value-of select="$sibling-before" />
</xsl:copy>
<!-- return my adjacent Bullettext siblings below me -->
<xsl:if test="local-name($sibling-before/following-sibling::*[1]) = 'Bullettext'">
<xsl:call-template name="get-all-adjacent-siblings">
<xsl:with-param name="sibling-before" select="$sibling-before/following-sibling::*[1]" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
我使用的输入是:
<?xml version="1.0" encoding="utf-8"?>
<Articles>
<Article>
<Bullettext>10,00 </Bullettext>
<Bullettext>8,00 </Bullettext>
</Article>
<Article>
<something>some text</something>
</Article>
<Article>
<Corpsdetexte>Bulgaria</Corpsdetexte>
<deeper>
<before>dogs</before>
<Bullettext>15,0 </Bullettext>
<Bullettext>10,0 </Bullettext>
<middle>cats</middle>
<Bullettext>25,0 </Bullettext>
<Bullettext>20,0 </Bullettext>
<after>cows</after>
</deeper>
</Article>
</Articles>
它给了我:
<?xml version="1.0" encoding="UTF-8"?>
<Articles>
<Article>
<LIST>
<Bullettext>10,00 </Bullettext>
<Bullettext>8,00 </Bullettext>
</LIST>
</Article>
<Article>
<something>some text</something>
</Article>
<Article>
<Corpsdetexte>Bulgaria</Corpsdetexte>
<deeper>
<before>dogs</before>
<LIST>
<Bullettext>15,0 </Bullettext>
<Bullettext>10,0 </Bullettext>
</LIST>
<middle>cats</middle>
<LIST>
<Bullettext>25,0 </Bullettext>
<Bullettext>20,0 </Bullettext>
</LIST>
<after>cows</after>
</deeper>
</Article>
</Articles>
如果你想进行其他转换,例如在同一个样式表中添加<p></p>
,但是如果你使用两个样式表进行两步转换,第一个执行上面的条件深层复制,然后是第二,使用第一个结果进行主要转换,你应该好好去。
答案 2 :(得分:1)
虽然目前这个问题有几个可行的答案,但实际上你可以用三个模板和身份很容易地对兄弟姐妹进行分组。
首先,您需要一个只删除所有节点并设置其优先级的模板,以便我们可以使用另一个模板覆盖它。
<xsl:template match="Bullettext" priority="1"/>
然后,定义一个模板,该模板匹配任何不在其自身之前的节点,为其分配更高的优先级。此模板将注入组,然后开始以不同的模式复制节点。
<xsl:template match="Bullettext[not(preceding-sibling::*[1][self::Bullettext])]" priority="2">
<LIST>
<xsl:apply-templates select="." mode="bullet-list"/>
</LIST>
</xsl:template>
最后,定义一个尾递归模板来处理被分组的项目。
<xsl:template match="Bullettext" mode="bullet-list">
<ITEM>
<xsl:apply-templates select="@*|node()"/>
</ITEM>
<xsl:apply-templates select="following-sibling::*[1][self::Bullettext]" mode="bullet-list"/>
</xsl:template>
以下是一个完整的样式表,它将对示例中的Bullettext元素进行分组:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<!-- Identity -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- Removes the Bullettext elements in the default mode. -->
<xsl:template match="Bullettext" priority="1" />
<!-- Creates the LIST elements around the removed Bullettext elements. -->
<xsl:template match="Bullettext[not(preceding-sibling::*[1][self::Bullettext])]" priority="2">
<LIST>
<xsl:apply-templates select="." mode="bullet-list" />
</LIST>
</xsl:template>
<!-- Converts sequential Bullettext elements into ITEM elements. -->
<xsl:template match="Bullettext" mode="bullet-list">
<ITEM>
<xsl:apply-templates select="@*|node()" />
</ITEM>
<xsl:apply-templates select="following-sibling::*[1][self::Bullettext]" mode="bullet-list" />
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:0)
尝试这样的事情:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Articles">
<LIST>
<xsl:for-each select="Article[1]/Bullettext">
<ITEM>
<xsl:value-of select="." />
</ITEM>
</xsl:for-each>
</LIST>
<p>
<something>
<xsl:value-of select="Article[2]/something" />
</something>
</p>
<p>
<Corpsdetexte>
<xsl:value-of select="Article[3]/Corpsdetexte" />
</Corpsdetexte>
</p>
<LIST>
<xsl:for-each select="Article[4]/Bullettext">
<ITEM>
<xsl:value-of select="." />
</ITEM>
</xsl:for-each>
</LIST>
</xsl:template>
</xsl:stylesheet>