Reputation: 1189
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
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