考虑以下XML结构:
<a>
<t>abc</t>
</a>
<a type="start"></a>
<b>
<t>ignore</t>
</b>
<a></a>
<a>
<t>1</t>
</a>
<a>
<t>2</t>
</a>
<a>
<t>3</t>
</a>
<b>
<t>ignore</t>
</b>
<a>
<t>4</t>
</a>
<a type="end"></a>
<a>
<t>def</t>
</a>
我需要获取属性值为a
和a
的{{1}}代码之间所有start
代码的内容总和。
我尝试使用以下XSL:
end
所需的输出是:
<xsl:template match="a">
<xsl:choose>
<xsl:when test="@type='start'">
<merged>
<xsl:call-template name="getMergedText">
<xsl:with-param name="text" select="''"/>
<xsl:call-template>
</merged>
</xsl:when>
<xsl:otherwise>
<single>
<xsl:value-of select="t"/>
</single>
</xsl:otherwise>
<xsl:choose>
</xsl:template>
<xsl:template name="getMergedText">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="following-sibling::a[1]/@type='end'">
<xsl:value-of select="$text"/>
</xsl:when>
<xsl:when test="following-sibling::a[1]/t">
<xsl:variable name="text.update">
<xsl:value-of select="$text"/>
<xsl:value-of select="following-sibling::a[1]/t"/>
</xsl:variable>
<xsl:for-each select="following-sibling::a[1]">
<xsl:call-template name="getMergedText">
<xsl:with-param name="text" select="$text.update"/>
<xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="following-sibling::a[1]">
<xsl:call-template name="getMergedText">
<xsl:with-param name="text" select="$text"/>
<xsl:call-template>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
我得到的输出是:
<single>abc</single>
<merged>1234</merged>
<single>def</single>
如何避免重新处理<single>abc</single>
<merged>1234</merged>
<single>1</single>
<single>2</single>
<single>3</single>
<single>4</single>
<single>def</single>
模板已处理过的a
个节点?
Thnx提前!!
注意:我正在使用XSLT 1.0。 XML中的起始端节点对可以有多个实例,在这些对之前,之后和之间有任意数量的节点。
答案 0 :(得分:3)
这里最有效的方法可能是我听说过的“兄弟递归”,使用尾递归模板来模拟“while循环”类型的结构。顶部模板首先处理第一个 a
元素,然后每个模板执行的最后一件事是将适当的模板应用于 next {{1} } element。
a
请注意<xsl:template match="---whatever matches the parent element of the a's---">
<xsl:apply-templates select="a[1]" />
</xsl:template>
<xsl:template match="a[@type = 'start']">
<!-- edge case - start followed immediately by end shouldn't generate
a "merged" element -->
<xsl:if test="not(following-sibling::a[1][@type = 'end'])">
<merged>
<xsl:apply-templates mode="merge" select="following-sibling::a[1]" />
</merged>
</xsl:if>
<!-- continue with the a after the "end" -->
<xsl:apply-templates select="
following-sibling::a[@type = 'end'][1]/following-sibling::a[1]" />
</xsl:template>
<xsl:template match="a">
<single><xsl:value-of select="t"/></single>
<xsl:apply-templates select="following-sibling::a[1]" />
</xsl:template>
<!-- stop the merge when we get the the "end" -->
<xsl:template match="a[@type = 'end']" mode="merge" />
<xsl:template match="a" mode="merge">
<xsl:value-of select="t" />
<xsl:apply-templates select="following-sibling::a[1]" mode="merge" />
</xsl:template>
之间的差异(检查紧随其后的following-sibling::a[1][@type = 'end']
元素是否为a
}和type="end"
(找到最近的following-sibling::a[@type = 'end'][1]
它有a
)。
答案 1 :(得分:1)
第二个想法,也许可以以一种更简单的方式完成:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='xml' version='1.0' encoding='utf-8' indent='yes'/>
<xsl:template match="/root">
<output>
<xsl:apply-templates
select="a[count(preceding-sibling::a[@type='start'])=count(preceding-sibling::a[@type='end'])]"/>
</output>
</xsl:template>
<xsl:template match="a[not(@type='start')]">
<single><xsl:value-of select="t" /></single>
</xsl:template>
<xsl:template match="a[@type='start']">
<xsl:variable name="prevStarts" select="count(preceding-sibling::a[@type='start'])" />
<group>
<xsl:apply-templates
select="following-sibling::a[count(preceding-sibling::a[@type='end'])=$prevStarts]"
mode="merge"/>
</group>
</xsl:template>
<xsl:template match="a" mode="merge">
<xsl:value-of select="t" />
</xsl:template>
</xsl:stylesheet>
以以下形式应用于修改后的输入:
<root>
<a><t>abc</t></a>
<a type="start"/>
<b><t>ignore</t>
</b>
<a/>
<a><t>1</t></a>
<a><t>2</t></a>
<a><t>3</t></a>
<b><t>ignore</t></b>
<a><t>4</t></a>
<a type="end"/>
<a><t>def</t></a>
<a><t>ghi</t></a>
<a type="start"/>
<b><t>ignore</t>
</b>
<a/>
<a><t>5</t></a>
<a><t>6</t></a>
<a><t>7</t></a>
<b><t>ignore</t></b>
<a><t>8</t></a>
<a type="end"/>
<a><t>jkl</t></a>
</root>
结果是:
<?xml version="1.0" encoding="utf-8"?>
<output>
<single>abc</single>
<group>1234</group>
<single>def</single>
<single>ghi</single>
<group>5678</group>
<single>jkl</single>
</output>