Reputation: 11
I'm new to XML and XSLT. I'm trying to separate a specific node in an XML file, separated with commas. I've seen a code that does it, and I tried to combine it in my XSLT file. It works, but the output contains all the nodes, and ignores my wished path. This is my XML file :
<?xml version="1.0" encoding="UTF-8"?>
<fortnight>
<hero>
<name>Guardian</name>
<characters>Bull,Knox,Penny</characters>
<rarities>Rare,Epic,Legendary</rarities>
</hero>
<hero>
<name>Deadly Blade</name>
<characters>Crash,Scorpion</characters>
<rarities>Epic,Legendary</rarities>
</hero>
<hero>
<name>Enforcer</name>
<characters>Grizzly</characters>
<rarities>Uncommon,Rare,Epic,Legendary</rarities>
</hero>
</fortnight>
and this is my XSLT file.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<characters>
<xsl:apply-templates select="hero" />
<xsl:value-of select="characters" />
</characters>
</xsl:template>
<xsl:template match="text()" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText) >0">
<character name="character">
<xsl:value-of select=
"substring-before(concat($pText, ','), ',')"/>
</character>
<xsl:call-template name="split">
<xsl:with-param name="pText" select=
"substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
How can I adjust my XSLT to create an output file, only with the "characters" node, and if a node contains commas, it will separate them in the output?
Upvotes: 1
Views: 104
Reputation: 243539
Although you haven't shown us the wanted result from the transformation, I believe this transformation might be useful:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="text()"/>
<xsl:template match="/">
<characters><xsl:apply-templates/></characters>
</xsl:template>
<xsl:template match="characters/text()" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText) > 0">
<character name="character">
<xsl:value-of select=
"substring-before(concat($pText, ','), ',')"/>
</character>
<xsl:call-template name="split">
<xsl:with-param name="pText" select=
"substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied to the provided source xml document:
<fortnight>
<hero>
<name>Guardian</name>
<characters>Bull,Knox,Penny</characters>
<rarities>Rare,Epic,Legendary</rarities>
</hero>
<hero>
<name>Deadly Blade</name>
<characters>Crash,Scorpion</characters>
<rarities>Epic,Legendary</rarities>
</hero>
<hero>
<name>Enforcer</name>
<characters>Grizzly</characters>
<rarities>Uncommon,Rare,Epic,Legendary</rarities>
</hero>
</fortnight>
the (most-likely) wanted result is produced:
<characters>
<character name="character">Bull</character>
<character name="character">Knox</character>
<character name="character">Penny</character>
<character name="character">Crash</character>
<character name="character">Scorpion</character>
<character name="character">Grizzly</character>
</characters>
Upvotes: 0
Reputation: 70638
When you do <xsl:apply-templates select="hero" />
you select all the child hero
elements under fortnight
and then XSLT looks for templates to apply to them. However, there are no templates matching hero
in your XSLT. When this happens, XSLT's built-in template rules apply. In this case, the following template would be applied
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
So, effectively, it is going to select everything in your XML until it gets to the text nodes. Not just the text nodes under characters
, but all text nodes.
What you need to do, is target only the nodes you wish to split. That means replacing <xsl:apply-templates select="hero" />
with <xsl:apply-templates select="hero/characters" />
.
Also note the line <xsl:value-of select="characters" />
can be removed. As this is in the template matching fortnight
This would only select characters
elements that were a direct child of fortnight
of which there are none.
Upvotes: 2