我尝试执行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来修复它,直到只剩下错误。
答案 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元素的模板。