XSLT:处理所有后续兄弟,直到满足某个条件,而不处理重新处理的节点

时间:2014-02-22 10:35:15

标签: xml xslt

考虑以下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>

我需要获取属性值为aa的{​​{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中的起始端节点对可以有多个实例,在这些对之前,之后和之间有任意数量的节点。

2 个答案:

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