There are several ways to do this.
I. Using a recursively-called named template in XSLT 1.0
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*[not(name()='tags')]">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="@tags">
<xsl:call-template name="tokenize">
<xsl:with-param name="pText"
select="concat(., ',')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pText"/>
<xsl:if test="string-length($pText)">
<tag>
<xsl:value-of select=
"substring-before($pText, ',')"/>
</tag>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the originally-provided XML document (corrected to be well-formed):
<post title="Hello World"
tags="Test,Hello,World" />
produces the required result:
<post>
<title>Hello World</title>
<tag>Test</tag>
<tag>Hello</tag>
<tag>World</tag>
</post>
II. Using the str-split-to-words
template/function from FXSL 1.x
Here FXSL provides the tokenization functionality:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*[not(name()='tags')]">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="@tags">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="."/>
<xsl:with-param name="pDelimiters"
select="','"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:template>
<xsl:template match="word">
<tag>
<xsl:value-of select="."/>
</tag>
</xsl:template>
</xsl:stylesheet>
When applied on the same XML document as before, the same correct output is produced.
III. Using the XPath 2.0 standard function tokenize()
from an XSLT 2.0 transformation
This is the easiest way -- if one can use an XSLT 2.0 processor.
The following XSLT 2.0 transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*[not(name()='tags')]">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="@tags">
<xsl:for-each select="tokenize(.,',')">
<tag><xsl:value-of select="."/></tag>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document again produces the wanted result.