I want to create a XSD structure from an Excel output using XSLT. But my XSLT does not generate the hierarchical structure, correctly. It has some additional nodes in an element
Here is a suggestion using a recursive function:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="mf">
<xsl:output indent="yes"/>
<xsl:function name="mf:group" as="element()*">
<xsl:param name="nodes" as="element(node)*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$nodes" group-starting-with="node[metaInfo/Level = $level and @groupMaxOccurs != '']">
<xs:element name="{@nodeTag}" minOccurs="{@minOccurs}" maxOccurs="{@groupMaxOccurs}" id="{metaInfo/ID}">
<xs:element name="{@nodeTag}" minOccurs="0" maxOccurs="1" id="{metaInfo/ID}"/>
<xsl:choose>
<xsl:when test="(current-group() except .)/metaInfo/Level = $level + 1">
<xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group() except ."/>
</xsl:otherwise>
</xsl:choose>
</xs:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="nodes">
<xs:schema>
<xsl:sequence select="mf:group(node, 1)"/>
</xs:schema>
</xsl:template>
<xsl:template match="node">
<xs:element name="{@nodeTag}" minOccurs="0" maxOccurs="{@segmentMaxOccurs}" id="{metaInfo/ID}"/>
</xsl:template>
</xsl:transform>
You will have to test yourself with more levels or provide some input samples with deeper nesting to allow us to test.
Let me suggest a different approach to creating the nested hierarchy. To demonstrate, I will use the following minimized input:
XML
<nodes>
<node nodeTag="A">
<metaInfo>
<Level>1</Level>
</metaInfo>
</node>
<node nodeTag="Ab">
<metaInfo>
<Level>2</Level>
</metaInfo>
</node>
<node nodeTag="Ab1">
<metaInfo>
<Level>3</Level>
</metaInfo>
</node>
<node nodeTag="Ab2">
<metaInfo>
<Level>3</Level>
</metaInfo>
</node>
<node nodeTag="Ac">
<metaInfo>
<Level>2</Level>
</metaInfo>
</node>
<node nodeTag="B">
<metaInfo>
<Level>1</Level>
</metaInfo>
</node>
<node nodeTag="C">
<metaInfo>
<Level>1</Level>
</metaInfo>
</node>
<node nodeTag="D">
<metaInfo>
<Level>1</Level>
</metaInfo>
</node>
<node nodeTag="Da">
<metaInfo>
<Level>2</Level>
</metaInfo>
</node>
<node nodeTag="Db">
<metaInfo>
<Level>2</Level>
</metaInfo>
</node>
</nodes>
Applying the following stylesheet:
XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="child-by-parent"
match="node"
use="generate-id(preceding-sibling::node[metaInfo/Level=current()/metaInfo/Level - 1][1])" />
<xsl:template match="nodes">
<root>
<xsl:apply-templates select="node[metaInfo/Level=1]"/>
</root>
</xsl:template>
<xsl:template match="node">
<element name="{@nodeTag}">
<xsl:apply-templates select="key('child-by-parent', generate-id())"/>
</element>
</xsl:template>
</xsl:stylesheet>
will return:
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element name="A">
<element name="Ab">
<element name="Ab1"/>
<element name="Ab2"/>
</element>
<element name="Ac"/>
</element>
<element name="B"/>
<element name="C"/>
<element name="D">
<element name="Da"/>
<element name="Db"/>
</element>
</root>
This works recursively with any number of levels.