XSLT transform creates StackoverflowException
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);
usi开发者_JAVA技巧ng (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">
<!-- 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.
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.
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.
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">
<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']]"
.
精彩评论