我正在尝试将XML文档转换为一个列表,其中的值基于前一个兄弟的属性。
示例XML:
<myRoot>
<Person>Craig</Person>
<Person rank="10">Woody</Person>
<Person>Brian</Person>
<Person>Michael</Person>
<Person rank="20">Emily</Person>
<Person>Chris</Person>
</myRoot>
我想要的是什么:
<myNewRoot>
<Index>1: Craig</Index>
<Index>10: Woody</Index>
<Index>11: Brian</Index>
<Index>12: Michael</Index>
<Index>20: Emily</Index>
<Index>21: Chris</Index>
</myNewRoot>
我陷入困境,无法确定最后一个先前的sibiling与@rank属性和当前节点之间的距离。
这是我当前的样式表
<xsl:template match="Person">
<xsl:element name="Index">
<xsl:choose>
<xsl:when test="./@rank">
<xsl:value-of select="./@rank"/>
</xsl:when>
<xsl:when test="preceding-sibling::Person[@rank]">
<xsl:value-of select="count(.|preceding-sibling::*[. > current()/preceding-sibling::Person[@rank][1]]) + preceding-sibling::Person[@rank][1]/@rank"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="position()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>: </xsl:text>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
我无法使count()功能正常工作并始终以
结束<myNewRoot>
<Index>1: Craig</Index>
<Index>10: Woody</Index>
<Index>11: Brian</Index>
<Index>11: Michael</Index>
<Index>20: Emily</Index>
<Index>21: Chris</Index>
</myNewRoot>
答案 0 :(得分:2)
解决方案是使用递归模板根据打印的先前值获取当前值,该值作为参数传递给模板。
<xsl:output method="xml" indent="yes" />
<xsl:template match="myRoot">
<myNewRoot>
<xsl:call-template name="make-index" />
</myNewRoot>
</xsl:template>
<xsl:template name="make-index">
<xsl:param name="element" select="Person" />
<xsl:param name="count" select="'0'" />
<!-- Continue if there is some element left -->
<xsl:if test="$element">
<!-- Obtain number to be printed next -->
<xsl:variable name="next-rank">
<xsl:choose>
<!-- If the rank attribute is present, output its value -->
<xsl:when test="$element[1]/@rank">
<xsl:value-of select="$element[1]/@rank" />
</xsl:when>
<!-- If the rank attribute is not present, increase the previous
printed value by one -->
<xsl:otherwise>
<xsl:value-of select="$count + 1" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Output the index along with the value of the current index -->
<Index>
<xsl:value-of select="concat($next-rank, ': ', $element[1])" />
</Index>
<!-- Recurse until we do not have any element left -->
<xsl:call-template name="make-index">
<xsl:with-param name="element" select="$element[position() > 1]" />
<xsl:with-param name="count" select="$next-rank" />
</xsl:call-template>
</xsl:if>
</xsl:template>
更新。以下解决方案不依赖于递归,可能不像前一个那样有效(在这一个中有更复杂的XPath操作)但是更短并且依赖于对兄弟姐妹进行分组,这与前一个不同。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="myRoot">
<myNewRoot>
<!-- Print all the elements before the first element with a rank
attribute defined -->
<xsl:apply-templates select="Person[(preceding-sibling::Person[@rank])][not(@rank)]" mode="print" />
<!-- Match all the elements with a rank attribute defined -->
<xsl:apply-templates select="Person[@rank]" />
</myNewRoot>
</xsl:template>
<!-- Match the set of Person elements with @rank defined -->
<xsl:template match="Person[@rank]">
<!-- Obtain id of current node before losing the context -->
<xsl:variable name="id" select="generate-id()" />
<!-- Match the current node along all the following siblings without @rank such
as their nearest preceding-sibling with @rank defined is the current
element, i.e all the elements between the current element and the next
element with @rank defined -->
<xsl:apply-templates select=".|following-sibling::Person[not(@rank)][generate-id(preceding-sibling::Person[@rank][1]) = $id]" mode="print">
<xsl:with-param name="rank" select="@rank" />
</xsl:apply-templates>
</xsl:template>
<!-- Print the information from a Person node, using rank to determine
the position -->
<xsl:template match="Person" mode="print">
<xsl:param name="rank" select="'1'" />
<Index>
<xsl:value-of select="concat($rank + position() - 1, ': ', .)" />
</Index>
</xsl:template>
注意:我假设您正在使用XSLT 1.0的两个解决方案。如果您使用XSLT 2.0,解决方案将比以前的解决方案更容易。