Search code examples
xmlxsltconverterstokenize

XSLT-1.0: Convert comma-separated values to element values


I am attempting to take an XML file and convert it with XSLT.
The XML I am trying to convert looks like this:

<root>
  <TAG>10, 1, 3, 123, 4001, 34, 200, 105, 54, 0, 0, 0</TAG>
</root>

When I run the conversion, I would like the result to appear like this:

<Field1>10</Field1>
<Field2>1</Field2>
...
<Field12>0</Field12>

However, my XSLT file is not working like designed.
Whenever I run the converter, I get back this as a response:

<Field_1>
  <TAG>10, 1, 3, 123, 4001, 34, 200, 105, 54, 0, 0, 0</TAG>
</Field_1>

Here is my XSLT file:

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

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

<xsl:template match="root">
    <xsl:call-template name="listItem">
        <xsl:with-param name="tag" select="TAG"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="listItem">
    <xsl:param name="features"/>
    <xsl:choose>
        <xsl:when test="contains($features, ',')">
            <xsl:element name="Field_{position()}">
                <xsl:apply-templates select="@*|node()"/>
                <xsl:value-of select="normalize-space(substring-before($features, ','))"/>
                <xsl:variable name="nextValue" select="substring-after($features, ',')"/>
            </xsl:element>
            <xsl:if test="normalize-space($nextValue)">
                <xsl:call-template name="listItem">
                    <xsl:with-param name="features" select="$nextValue"/>
                </xsl:call-template>
            </xsl:if>
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="Field_{position()}">
                <xsl:apply-templates select="@*|node()"/>
                <xsl:value-of select="$features"/>
            </xsl:element>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
</xsl:stylesheet>

Does anyone have any suggestions for how to get my XSLT file to convert my XML into the desired result?
Please and thanks for your help.


Solution

  • Use this XSLT.
    It is XSLT version 1.0 and uses recursion via a named <xsl:template> called field to delimit the comma-separated values and encapsulate each of the values in <Field> elements. The element's name is generated of the static string Field plus a recursively passed variable named cnt.

    <?xml version="1.0" ?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" />
    
      <xsl:template match="/root">
        <root>
          <xsl:call-template name="field">
            <xsl:with-param name="cnt" select="1" />
            <xsl:with-param name="txt" select="concat(TAG/text(),',')" />
          </xsl:call-template>
        </root>
    </xsl:template>
    
      <xsl:template name="field">
        <xsl:param name="cnt" />
        <xsl:param name="txt" />
        <xsl:element name="{concat('Field',$cnt)}">
          <xsl:value-of select="normalize-space(substring-before($txt,','))"/>
        </xsl:element>
        <xsl:if test="normalize-space(substring-after($txt,',')) != ''">
          <xsl:call-template name="field">
            <xsl:with-param name="cnt" select="$cnt + 1" />
            <xsl:with-param name="txt" select="substring-after($txt,',')" />
          </xsl:call-template>
        </xsl:if>
      </xsl:template>
    
    </xsl:stylesheet>
    

    The result - as desired - is:

    <?xml version="1.0"?>
    <root>
        <Field1>10</Field1>
        <Field2>1</Field2>
        <Field3>3</Field3>
        <Field4>123</Field4>
        <Field5>4001</Field5>
        <Field6>34</Field6>
        <Field7>200</Field7>
        <Field8>105</Field8>
        <Field9>54</Field9>
        <Field10>0</Field10>
        <Field11>0</Field11>
        <Field12>0</Field12>
    </root>