Elad Levi
Elad Levi

Reputation: 11

XML and XSLT - separate node includes commas

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

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

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

Tim C
Tim C

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

Related Questions