I am looking for some help to convert the following format XML using XSLT to the desired output -
Input XML:
<properties>
<entry>
<key>first_node</key>
<value>GDP</value>
</entry>
<entry>
<key>parent_node/second_node/third_node</key>
<value>INR</value>
</entry>
<entry>
<key>fourth_node</key>
<value>
<genericData>
<identifier>fourth_node</identifier>
<properties>
<entry>
<key>fifth_node/sixth_node</key>
<value>USD</value>
</entry>
<entry>
<key>seventh_node</key>
<value>EUR</value>
</entry>
</properties>
</genericData>
</value>
</entry>
<entry>
<key>eigth_node</key>
<value>
<genericData>
<identifier>eigth_node</identifier>
<properties>
<entry>
<key>ninth_node</key>
<value>SGD</value>
</entry>
<entry>
<key>tenth_node</key>
<value>PSO</value>
</entry>
</properties>
</genericData>
</value>
</entry>
</properties>
Desired Output:
<properties>
<first_node>GDP</first_node>
<parent_node>
<second_node>
<third_node>INR</third_node>
</second_node>
</parent_node>
<fourth_node>
<fifth_node>
<sixth_node>USD</sixth_node>
</fifth_node>
<seventh_node>EUR</seventh_node>
</fourth_node>
<eight_node>
<ninth_node>SGD</ninth_node>
<tenth_node>PSO</tenth_node>
</eight_node>
</properties>
I have modified the question with a possible input XML where the format is changed a bit.
Kindly help with the XSLT Version 1 to get the desired output.
Thank you very much for your help.
In XSLT 2 or 3 you can use the XPath tokenize
function to construct a sequence of strings with the element names in a recursive template constructing the nested elements as needed:
<xsl:template match="entry">
<xsl:param name="element-names" as="xs:string*" select="tokenize(string[1], '/')"/>
<xsl:variable name="element-name" as="xs:string?" select="head($element-names)"/>
<xsl:choose>
<xsl:when test="$element-name">
<xsl:element name="{$element-name}">
<xsl:apply-templates select=".">
<xsl:with-param name="element-names" select="tail($element-names)"/>
</xsl:apply-templates>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="string[2]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Full XSLT 3 stylesheet would be
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="entry">
<xsl:param name="element-names" as="xs:string*" select="tokenize(string[1], '/')"/>
<xsl:variable name="element-name" as="xs:string?" select="head($element-names)"/>
<xsl:choose>
<xsl:when test="$element-name">
<xsl:element name="{$element-name}">
<xsl:apply-templates select=".">
<xsl:with-param name="element-names" select="tail($element-names)"/>
</xsl:apply-templates>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="string[2]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Example at https://xsltfiddle.liberty-development.net/3NzcBtK, for XSLT 2 you would need to spell out the identity transformation and I think instead of using head($element-names)
you would need to use element-names[1]
and instead of the tail
call on that variable you would need $element-names[position() gt 1]
.
In XSLT 1.0, if you can't use any extension functions, you can achieve it by using a recursive named template to split up the path
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entry">
<xsl:call-template name="splitter">
<xsl:with-param name="path" select="key" />
<xsl:with-param name="value" select="value" />
</xsl:call-template>
</xsl:template>
<xsl:template name="splitter">
<xsl:param name="path" />
<xsl:param name="value" />
<xsl:choose>
<xsl:when test="contains($path, '/')">
<xsl:element name="{substring-before($path, '/')}">
<xsl:call-template name="splitter">
<xsl:with-param name="path" select="substring-after($path, '/')" />
<xsl:with-param name="value" select="$value" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$path}">
<xsl:choose>
<xsl:when test=".//entry">
<xsl:apply-templates select=".//entry" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value" />
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/jyH9rMX