tags:

views:

71

answers:

3

I tried to perform XSLT transform of a XSD file. My goal is in the end to create SQL from XSD. So far so good, this is what I have:

void Convert()
{
            XPathDocument xpathDoc = new XPathDocument(@"myschema.xsd");
            string xslPath = @"convert.xsl";
            XslCompiledTransform transform = new XslCompiledTransform();               
            transform.Load(xslPath, new XsltSettings(true, true), null);    
            using (FileStream fs = File.Create(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "output.sql")))
            {
                try
                {
                    transform.Transform(xpathDoc, null, fs);
                }
                catch
                {
                    fs.Close();
                }
            }
}

This is the XSLT file which is failing:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
               version="1.0" 
               xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;

<!-- Get schema nodes from this schema and any included schemas -->
<xsl:variable name="contents" select="/|document(//xs:include/@schemaLocation)" />

<xsl:template match="*" >

 <xsl:for-each select="$contents" >
  <xsl:apply-templates select=".//xs:element" />
 </xsl:for-each>

</xsl:template>

<xsl:template match="xs:element">

    <xsl:apply-templates />

</xsl:template>

</xsl:stylesheet>

I always get a StackoverflowException in System.Data.SqlXml.dll. How can I stop the recursion? Shouldn't it stop if no xs:element remain?

EDIT: The original code was from here and it already had the error. I tried to fix it by simplifying the XSLT until only the error remained.

+2  A: 

the line

<xsl:apply-templates select=".//xs:element" />

sends the current node (xs:element) to the template it started from. Then it matches it in the for loop and sends itself again. Stack overflow is inevitable.

Woody
A: 

As Woody has answer, you have a circular call ("For every element... apply templates for elements"). So, the proper way is:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
    <xsl:template match="/" name="root">
        <xsl:param name="schema" select="*/*"/>
        <xsl:choose>
            <xsl:when test="$schema[self::xs:include]">
                <xsl:call-template name="root">
                    <xsl:with-param name="schema" select="$schema[not(self::xs:include)]|document($schema[self::xs:include]/@schemaLocation)/*/*"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="*/*">
                    <xsl:with-param name="schema" select="$schema"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

With this stylesheet you need to add your templates with param schema been your expanded schema. Also, you need to apply templates with a param schema as select="$schema".

EDIT: Sorry, a little mistake. Also, an explanation: when you process a modular schema you need to get first the complete expanded schema because otherwise you end up calling a recursion template for getting reference and type definitions in diferent schema modules every time. With my template you get the complete expanded schema in $schema param, so when you process a xs:element with @type="someType" you could continue the process with xsl:apply-templates select="$schema[self::xs:complexType[@name='someType']]".

Alejandro
A: 

The problem that causes the endless recursion is here:

<xsl:template match="xs:element">  

    <xsl:apply-templates />  

</xsl:template>

The <xsl:apply-templates> instruction will cause other elements than xs:element to be processed. For all such elements the following template is selected for processing:

<xsl:template match="*" >     

 <xsl:for-each select="$contents" >     
  <xsl:apply-templates select=".//xs:element" />     
 </xsl:for-each>     

</xsl:template>  

and this closes the loop and causes the endless recursion.

This problem can be avoided in the following way:

  <xsl:template match="xs:include">
   <xsl:apply-templates select="document(@schemaLocation)/*/>
  </xsl:template>

No other special templates are necessary -- just add the templates that process specific xsd elements.

Dimitre Novatchev
@Dimitre: your answer is the best explanation for the endles recursion. But it does not cover the "Get schema nodes from this schema and any included schemas" part.
Alejandro
@Alejandro: Thanks, I think I've fixed it now.
Dimitre Novatchev