在 Saxon 中处理无限递归 XSL

Posted

技术标签:

【中文标题】在 Saxon 中处理无限递归 XSL【英文标题】:Handling infinite recursion XSL in Saxon 【发布时间】:2016-08-13 22:47:39 【问题描述】:

我知道这个问题可能超出了 Saxon 的范围,并且与使用它进行转换的应用程序架构更相关,但只是想尝试一下。考虑以下文件-

XML

<?xml version="1.0" encoding="UTF-8"?>
<document>
    string
</document>

XSL

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xsl xs">

    <xsl:template match="/"> 
       <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="node()">
        <xsl:apply-templates select="."/>
    </xsl:template>

</xsl:stylesheet>

XSL 将在转换(即堆栈溢出)期间进入无限递归。我的问题是 - 有没有办法阻止或防止这种类型的转换进入无限递归?任何可以添加到命令行的参数可以触发警告并正常停止?

【问题讨论】:

我最喜欢的 xslt 处理器 xsltproc 有:--maxdepth 值 在 libxslt 断定它处于无限循环之前调整模板堆栈的最大深度。默认值为 500 您需要查看-quit:(on|off) 选项,该选项确定Saxon 是退出JVM 还是在失败时引发运行时异常。如果从 Java 调用 Saxon,后者会很有帮助。如果有一种方法可以静态检测或阻止无限递归,那么计算机科学将会大不相同。 (我的意思是:不,撒克逊人没有它,因为图灵证明了它是不可能的。) Java VM 检测到堆栈溢出,Saxon 拦截该异常,如果可以的话,尝试用递归模板调用来解释它。但是堆栈溢出和无限递归并不是一回事。在这个特定的示例中,Saxon 使用了一种称为尾调用优化的技术,它将递归转换为循环;这是故意设计为在不耗尽可用堆栈空间的情况下启用任意深度递归,其结果是该程序不会抛出堆栈溢出异常,而是永远运行。这当然是检测不到的。 @hr_117 谢谢!该设置类似于我正在寻找的设置。我使用的大多数 XSL 都是 3.0 版,我认为 xsltproc 不支持该版本 @FoxyBOA Nope..还是个问题:( 【参考方案1】:

您可能只想创建自己的,而不是依赖现有设置来解决此类问题。

考虑 以下 XSL 针对您提供的非常简单的 XML 运行:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xsl xs">

    <xsl:variable name="recursion.limit" select="500" as="xs:integer"/>
    <xsl:variable name="new.line" select="'&#x0A;'" as="xs:string"/>

    <xsl:template match="/">
        <xsl:value-of select="$new.line"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="node()">
        <xsl:param name="recursion.count" select="1" as="xs:integer"/>

        <xsl:choose>
            <xsl:when test="$recursion.count &lt;= $recursion.limit">
                <xsl:value-of select="'&lt;' || name() || '&gt;' || ':' || $recursion.count || $new.line" disable-output-escaping="yes"/>
                <xsl:apply-templates select=".">
                    <xsl:with-param name="recursion.count" select="$recursion.count + 1" as="xs:integer"/>
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message>
                    <xsl:value-of select="'Recursion limit of ' || $recursion.limit|| ' hit.'"/>
                </xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

这肯定会阻止或阻止这种类型的转换进入无限递归,但这当然不是自动的。您必须在代码中进行设置。但如果完成,这可以作为自制的最大深度设置。此时您所要做的就是将工作表参数化为采用这样的值,而不是像我一样将其烘焙,这就是您的设置。这满足了您对可以添加到命令行的参数的需求,这些参数可以触发某种警告和/或优雅地停止。它是纯 XSL,因此应该独立于引擎,前提是您选择的引擎(我真的希望是 Saxon)正确满足 XSL 规范。

【讨论】:

【参考方案2】:

您是否尝试过 -opt:0 来禁用优化?

【讨论】:

以上是关于在 Saxon 中处理无限递归 XSL的主要内容,如果未能解决你的问题,请参考以下文章

XSL 递归调用 - xsl:functions vs xsl:template with call template

Saxon-js 是不是对 xsl:param 执行 XML 语法检查?

使用 Saxon-JS 及其全局 Javascript 函数命名空间在 XSL 中调用 jQuery-UI 组件?

XSLT/XSL 递归嵌套元素

XSLT 2.0 中的尾递归函数不起作用

递归函数和预处理学习总结