Search code examples
xsltxslt-1.0xslt-grouping

selecting every node and siblings until next occurrence


With this xml structure :

<doc>
    <members>
    <member name="T:XXX">
    </member>
    <member name="F:YYY">
    </member>
    <member name="P:ZZZ">
    </member>
    <member name="T:XXX">
    </member>
</doc>

I try to get all nodes following node with name attribute starting with 'T:' until the next node with name attribute starting with 'T:'.

Based on this stackoverflow topic (#40767321), I found an almost perfect answer.

With the xsl:key below, it takes the first T: node and all of the followers but it also includes the next T: node in the select. How can I exlude it ?

<xsl:key name="subMembers" match="member" use="generate-id(preceding-sibling::*[contains(@name, 'T:')][1])" />

Thanks for your help !


Solution

  • In the linked topic, which deals with ol elements followed by div elements, the key is only matching the div elements. However, in your question, the T: nodes represent the ol elements and the key is matching these elements.

    You need to replace the key to ignore the T: nodes

    <xsl:key name="subMembers" match="member[not(starts-with(@name, 'T:'))]" use="generate-id(preceding-sibling::*[starts-with(@name, 'T:')][1])" />
    

    So, assuming you start off by selecting the T: nodes, you would select those nodes, and the associated nodes, like so:

     <xsl:copy-of select="self::*|key('subMembers', generate-id())" />
    

    (Or you could use xsl:for-each or xsl:apply-templates should you also wish to transform the group elements).

    Try this XSLT

    <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:key name="subMembers" match="member[not(starts-with(@name, 'T:'))]" use="generate-id(preceding-sibling::*[starts-with(@name, 'T:')][1])" />
    
    <xsl:template match="members">
        <members>
            <xsl:for-each select="member[starts-with(@name, 'T:')]">
                <group>
                    <xsl:copy-of select="self::*|key('subMembers', generate-id())" />
                </group>
            </xsl:for-each>
        </members>
    </xsl:template>
    
    </xsl:stylesheet>