XSLT转换创建StackoverflowException

时间:2010-07-21 13:58:50

标签: c# .net sql xslt xsd

我尝试执行XSD文件的XSLT转换。我的目标是最终从XSD创建SQL。到目前为止一切顺利,这就是我所拥有的:

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();
                }
            }
}

这是失败的XSLT文件:

<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>

我总是在System.Data.SqlXml.dll中遇到StackoverflowException。我怎么能停止递归?如果没有xs:元素,它不应该停止吗?

修改 原始代码来自here,它已经有错误。我试图通过简化XSLT来修复它,直到只剩下错误。

3 个答案:

答案 0 :(得分:4)

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

将当前节点(xs:element)发送到它开始的模板。然后它在for循环中匹配它并再次发送自己。堆栈溢出是不可避免的。

答案 1 :(得分:0)

正如伍迪所回答的那样,你有一个循环调用(“为每个元素......应用元素模板”)。所以,正确的方法是:

<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>

使用此样式表,您需要使用param schema添加模板作为扩展架构。此外,您需要将模板schema应用为select="$schema"

编辑:抱歉,有点错误。另外,解释一下:当您处理模块化模式时,您需要首先获得完整的扩展模式,否则您最终会调用一个递归模板,以便每次都在不同的模式模块中获取引用和类型定义。使用我的模板,您可以在$schema param中获得完整的展开式架构,因此当您使用xs:element处理@type="someType"时,可以使用xsl:apply-templates select="$schema[self::xs:complexType[@name='someType']]"继续此过程。

答案 2 :(得分:0)

导致无休止递归的问题在于:

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

    <xsl:apply-templates />  

</xsl:template>

<xsl:apply-templates>指令将导致处理除xs:element之外的其他元素。对于所有这些元素,选择以下模板进行处理:

<xsl:template match="*" >     

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

</xsl:template>  

这会关闭循环并导致无限递归。

可以通过以下方式避免此问题

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

不需要其他特殊模板 - 只需添加处理特定xsd元素的模板。