Search code examples
xmlxsltxml-parsingxslt-1.0xslt-2.0

Dynamic XML Element name from ValueList


May I ask if below is doable? Given we have below ugly (but valid) XML which contains valueList in its elements:

<LIST>
    <COLUMN>    COL1    COL2    COL3    COLn    </COLUMN>
    <VALUE>     val1    val2    val3    valn    </VALUE>
    <VALUE>     val4    val5    val6    valn2   </VALUE>
</LIST>

Is it possible to use XSLT to transform it into this format?

<LIST>
    <VALUE>
        <COL1>val1</COL1>
        <COL2>val2</COL2>
        <COL3>val3</COL3>
        <COLn>valn</COLn>
    </VALUE>
    <VALUE>
        <COL1>val4</COL1>
        <COL2>val5</COL2>
        <COL3>val6</COL3>
        <COLn>valn2</COLn>
    </VALUE>
</LIST>

What I have done in this sample is below.. but couldn't get valuelist of Column elements to be a dynamic element for the values..

<xsl:template name="Sample">
    <LIST>
    <xsl:for-each select="$srcDelete/LIST/VALUE">
        <VALUE>         
            <xsl:for-each select="tokenize(normalize-space(.),' ')">
                <item><xsl:value-of select="."/></item>
            </xsl:for-each>               
        </VALUE>
    </xsl:for-each>
    </LIST>
</xsl:template> 

<xsl:template match="/">
         <xsl:call-template name="Sample"/>  
</xsl:template>

... its output:

<?xml version="1.0" encoding="UTF-8"?>
<LIST>
   <VALUE>
      <item>val1</item>
      <item>val2</item>
      <item>val3</item>
      <item>valn</item>
   </VALUE>
   <VALUE>
      <item>val4</item>
      <item>val5</item>
      <item>val6</item>
      <item>valn2</item>
   </VALUE>
</LIST>

Solution

  • You can tokenize the column names as well and then use them:

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
    <xsl:output indent="yes"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
    <xsl:template match="LIST">
        <LIST>
        <xsl:variable name="col-names" select="for $name in tokenize(COLUMN, '\s+')[normalize-space()] return normalize-space($name)"/>
        <xsl:for-each select="VALUE">
            <VALUE>         
                <xsl:for-each select="tokenize(normalize-space(.),' ')">
                    <xsl:variable name="pos" select="position()"/>
                    <xsl:element name="{$col-names[$pos]}">
                        <xsl:value-of select="."/>
                    </xsl:element>
                </xsl:for-each>               
            </VALUE>
        </xsl:for-each>
        </LIST>
    </xsl:template>
    </xsl:transform>
    

    Online at http://xsltransform.net/94rmq6N.