XSLT: process all subsequent siblings until a condition is met if the processed nodes are not reprocessed

Consider the following XML structure:

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

I need to get the sum of the contents of all tags abetween tags awith attribute value startand end.

I tried it with the following XSL:

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

Required Conclusion:

<single>abc</single>
<merged>1234</merged>
<single>def</single>

The output I get is:

<single>abc</single>
<merged>1234</merged>
<single>1</single>
<single>2</single>
<single>3</single>
<single>4</single>
<single>def</single>

How can I avoid reprocessing nodes aalready processed by the template getMergedText?

Thnx in advance!

Note. I am working with XSLT 1.0. In XML, there can be several instances of pairs of starting end nodes with any number of nodes before, after, and between pairs.

+3
source share
2 answers

, , " ", "while loop". a, , , 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, type="end").

+3

, , :

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

All Articles