Search code examples
xmlxsltxml-parsingxslt-1.0xslt-2.0

XSLT: How to replace the first sibling with a concatenation of all siblings?


I'm having a hard time trying to implement an XSL transformation.

I need to transform this:

<records>
    <item>
        <id type="uid">1</id>
        <name>Homepage</name>
        <attr>AB308E</attr>
    </item>
    <item>
        <id type="uid">5</id>
        <name>Electronics</name>
        <attr>F04550</attr>
    </item>
    <item>
        <id type="uid">8</id>
        <name>Accessories</name>
        <attr>00EE80</attr>
    </item>
</records>

into this:

<records>
    <item>
        <id type="uid">1</id>
        <category>Homepage - Electronics - Accessories</category>
        <attr>AB308E</attr>
    </item>
    <item>
        <id type="uid">5</id>
        <name>Electronics</name>
        <attr>F04550</attr>
    </item>
    <item>
        <id type="uid">8</id>
        <name>Accessories</name>
        <attr>00EE80</attr>
    </item>
</records>

I know it doesn't make a lot of sense semantically speaking, but that's a hack I need in order to inject data in a specific manner into some interface.

Rule #1: the name tag of the first item of each records tag (there are many records in the actual file) turns into category and contains the concatenation of all item's names from the current records scope

Rule #2: item tags that are not first child of records are unchanged.

I tried using <xsl:value-of select="concat(' - ', .)"/> rules but had no luck.

Would anyone know how to achieve this?


Solution

  • Try this:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output method="xml" indent="yes"/>
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="records/item[1]/name">
            <category>        
                <xsl:for-each select="../../item/name">
                    <xsl:value-of select="." />
                    <xsl:if test="position() != last()">
                        <xsl:value-of select="' - '" />
                    </xsl:if>
                </xsl:for-each>
            </category>
        </xsl:template>
    </xsl:stylesheet>