Paulb
Paulb

Reputation: 1531

XSL: Trouble with Sibling Axis. Is position() the answer?

I have xml that approximates:

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <num>Book 1.</num>
    <head> Title</head>
    <chapter>
        <num>1.</num>
        <head> The Begining</head>
        <p>content</p>
    </chapter>
    <num>12. </num><p>we want that number untouched</p>
    <chapter>
        <num>2.</num>
        <head> The Middle</head>
        <p>content</p>
    </chapter>
    <head>Heads Occur</head><p>we want that head untouched</p>
</book>

Where a <num> is followed immediately by a <head> I want to merge the two together. I use this xsl, with some success, but not in all use-cases.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="num[following-sibling::head]">
        <mergedhead>
            <xsl:apply-templates select="node()|@*"/>
            <xsl:value-of select="following-sibling::head"/>
        </mergedhead>    
    </xsl:template>
    <!-- keep the old head from showing up in the new output-->
    <xsl:template match="head[preceding-sibling::num]"/>

</xsl:stylesheet>

following::sibling and preceding::sibling work, but not in all use-cases. Sometimes they pull in <num> and <head> that are not directly next to each other. Output of my flawed XSL:

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <mergedhead>Book 1. Title</mergedhead>
    <chapter>
        <mergedhead>1. The Begining</mergedhead>
        <p>content</p>
    </chapter>
    <mergedhead>12. Heads Occur</mergedhead><p>we want that number untouched</p>
    <chapter>
        <mergedhead>2. The Middle</mergedhead>
        <p>content</p>
    </chapter>
    <p>we want that head untouched</p>
</book>

You can see that it merged the #12, which I want untouched, with the 'heads occur' which I also want untouched. I know it's because they are siblings, even there are other nodes in between. I think the answer I want is in position(). But I have not been successful with that.

For reference, the desired out is below:

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <mergedhead>Book 1. Title</mergedhead>
    <chapter>
        <mergedhead>1. The Begining</mergedhead>
        <p>content</p>
    </chapter>
    <num>12. </num><p>we want that number untouched</p>
    <chapter>
        <mergedhead>2. The Middle</mergedhead>
        <p>content</p>
    </chapter>
    <head>Heads Occur</head><p>we want that head untouched</p>
</book>

Upvotes: 0

Views: 101

Answers (1)

Jirka Š.
Jirka Š.

Reputation: 3428

Try following xslt if it fulfils your requirement.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <!-- Take all following sibling elements (i.e. following-sibling:*) regardless their names, from them take first one (i.e. [1]) and test it if it is head (i.e. [self::head]-->
    <xsl:template match="num[following-sibling::*[1][self::head]]">
        <mergedhead>
            <xsl:apply-templates select="node()|@*"/>
            <!-- Take the first following sibling -->
            <xsl:value-of select="following-sibling::head[1]"/>
        </mergedhead>    
    </xsl:template>
    <!-- similar to num template -->
    <xsl:template match="head[preceding-sibling::*[1][self::num]]"/>

</xsl:stylesheet>

Upvotes: 2

Related Questions