cerbin
cerbin

Reputation: 1189

Separating string by semicolon into xml elements using xslt

I have transformated xml:

<import>
    <issue>
        <article>
            <languageVersion>
                <title>some title</title>
                <keywords>keyword1; keyword2; keyword3</keywords>
            </languageVersion>
        </article>
        <article>
            <languageVersion>
                <title>some title</title>
                <keywords>keyword1; keyword2; keyword3</keywords>
            </languageVersion>
        </article>
    </issue>
</import>

And now I want to separate all words in tag by semicolon using xslt transformation, so the target xml would be like this:

<import>
    <issue>
        <article>
            <languageVersion>
                <title>some title</title>
                <keywords>
                    <keyword>keyword1</keyword>
                    <keyword>keyword2</keyword>
                    <keyword>keyword3</keyword>
                </keywords>
            </languageVersion>
        </article>
        <article>
            <languageVersion>
                <title>some title</title>
                <keywords>
                    <keyword>keyword1</keyword>
                    <keyword>keyword2</keyword>
                    <keyword>keyword3</keyword>
                </keywords>
            </languageVersion>
        </article>
        <article>
    </issue>
</import>

How can I do this using XSLT transformation? I tried already already using code from there but unfortunatelly the code was destroying rest of xml elements:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="keywords">
        <keywords>
            <xsl:apply-templates/>
        </keywords>
    </xsl:template>
    <xsl:template match="keywords" name="split">
        <xsl:param name="pText" select="."/>
        <xsl:param name="pItemElementName" select="'keyword'"/>
        <xsl:if test="string-length($pText) > 0">
            <xsl:variable name="vNextItem" select=
      "substring-before(concat($pText, ';'), ';')"/>
            <xsl:element name="{$pItemElementName}">
                <xsl:value-of select="$vNextItem"/>
            </xsl:element>
            <xsl:call-template name="split">
                <xsl:with-param name="pText" select="substring-after($pText, ';')"/>
                <xsl:with-param name="pItemElementName" select="$pItemElementName"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Views: 1026

Answers (1)

Tim C
Tim C

Reputation: 70618

You have two templates matching keywords which is considered an error in XSLT if they have the same priority. According to the spec...

An XSLT processor may signal the error; if it does not signal the error, it must recover by choosing, from amongst the matching template rules that are left, the one that occurs last in the stylesheet.

So, in your case, it is only the second template that is being used.

However, all you need to do is remove the "match" on the the second template to make it just a named template, and change the first template to call the named template directly. You should also add the identity template, to take care of existing elements, and then your job will be done

Try this XSLT

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="keywords">
        <xsl:copy>
            <xsl:call-template name="split" />
        </xsl:copy>
    </xsl:template>

    <xsl:template name="split">
        <xsl:param name="pText" select="."/>
        <xsl:param name="pItemElementName" select="'keyword'"/>
        <xsl:if test="string-length($pText) > 0">
            <xsl:variable name="vNextItem" select=
      "substring-before(concat($pText, ';'), ';')"/>
            <xsl:element name="{$pItemElementName}">
                <xsl:value-of select="$vNextItem"/>
            </xsl:element>
            <xsl:call-template name="split">
                <xsl:with-param name="pText" select="substring-after($pText, ';')"/>
                <xsl:with-param name="pItemElementName" select="$pItemElementName"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 3

Related Questions