【问题标题】:Optimize <xsl:choose> with many <xsl:when>用许多 <xsl:when> 优化 <xsl:choose>
【发布时间】:2010-07-12 23:28:02
【问题描述】:

我有一系列中等大小的XML文档,主要是文本,有几个节点代表要扩展的宏,例如:

<foo>Some text <macro>A1</macro> ... <macro>B2</macro> ...etc...</foo>

我的目标是用相应的 XML 替换每个宏。通常它是一个具有不同属性的&lt;img&gt; 标签,但也可以是其他一些 HTML。

样式表是以编程方式生成的,一种方法是为每个宏设置一个模板,例如

<xsl:template match="macro[.='A1']">
    <!-- content goes here -->
</xsl:template>
<xsl:template match="macro[.='A2']">
    <!-- other content goes here -->
</xsl:template>
<xsl:template match="macro[.='B2']">
    <!-- etc... -->
</xsl:template>

它工作得很好,但它最多可以有一百个宏,而且性能不是很好(我正在使用 libxslt。)我尝试了几种替代方法,例如:

<xsl:template match="macro">
    <xsl:choose>
        <xsl:when test=".='A1'">
            <!-- content goes here -->
        </xsl:when>
        <xsl:when test=".='A2'">
            <!-- other content goes here -->
        </xsl:when>
        <xsl:when test=".='B2'">
            <!-- etc... -->
        </xsl:when>
    </xsl:choose>
</xsl:template>

它的性能略高一些。我尝试添加另一个级别的分支,例如:

<xsl:template match="macro">
    <xsl:choose>
        <xsl:when test="substring(.,1,1) = 'A'">
            <xsl:choose>
                <xsl:when test=".='A1'">
                    <!-- content goes here -->
                </xsl:when>
                <xsl:when test=".='A2'">
                    <!-- other content goes here -->
                </xsl:when>
            </xsl:choose>
        </xsl:when>
        <xsl:when test=".='B2'">
            <!-- etc... -->
        </xsl:when>
    </xsl:choose>
</xsl:template>

它的加载速度稍慢(XSL 更大且更复杂),但执行速度稍快(每个分支可以消除几种情况。)

现在我想知道,有没有更好的方法来做到这一点?我有大约 50-100 个宏。通常,转换是使用 libxslt 执行的,但我不能使用来自其他 XSLT 引擎的专有扩展。

欢迎任何意见:)

【问题讨论】:

    标签: optimization xslt


    【解决方案1】:

    这将是另一种方式:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
        <xsl:variable name="dummy">
                <mac id="A1">success</mac>
                <mac id="A2">fail</mac>
                <mac id="B1">This <b>fail</b></mac>
                <mac id="B2">This <b>success</b></mac>
        </xsl:variable>
        <xsl:key name="macro" match="mac" use="@id"/>
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="macro">
            <xsl:variable name="me" select="."/>
            <xsl:for-each select="document('')">
                <xsl:copy-of select="key('macro',$me)/node()"/>
            </xsl:for-each>
        </xsl:template>
    </xsl:stylesheet>
    

    注意:这是性能:XML parse 1,805ms, XSL parse 0,483ms, Transform 0,215ms

    【讨论】:

    • 奇怪的是,我已经尝试过类似的方法,但没有成功,因为document('') 将源文档的根返回给我,而不是样式表的根。我刚刚检查了 XSLT 规范,确实,它应该返回样式表的根,所以要么我发现了一个错误,要么我在某个地方搞砸了。我会再试一次,让你更新,谢谢。
    • 确实,这是我遇到的一个已知 libxslt 错误 (bugzilla.gnome.org/show_bug.cgi?id=549552),不过我让它工作了,而且性能似乎与我列出的第二种方法相当。如果重复使用相同的宏,它似乎可以更好地扩展。
    【解决方案2】:

    如果您的宏是固定的 XML,您可以有一个类似于以下内容的 macros.xml 文档:

    <?xml version="1.0"?>
    <macros>
        <macro name="A1"><!-- content goes here --></macro>
        <macro name="A2"><!-- content goes here --></macro>
    </macros>
    

    然后您可以:

    <xsl:template match="macro">
        <xsl:variable name="name" select="text()"/>
        <xsl:apply-templates select="document(macros.xml)/macros/macro[@name=$name]" mode="copy"/>
    </xsl:template>
    
    <xsl:template match="*" mode="copy">
        <xsl:copy><xsl:copy-of select="*"/>
            <xsl:apply-templates mode="copy"/>
        </xsl:copy>
    </xsl:template>
    

    这会提高性能吗?

    【讨论】:

    • 我已经尝试过&lt;xsl:template match="macro"&gt;&lt;xsl:copy-of select="document('macros.xml')/macros/macro[@name=current()]/node()" /&gt;&lt;/xsl:template&gt;&lt;/xsl:stylesheet&gt;,但性能更差,考虑到它必须访问外部资源并在其上运行一些 XPath,这是可以预料的。也许我可以在某处使用 xsl:key 再试一次......不过还是谢谢 :)
    • 您是否尝试将文档加载到变量中并使用它——不过,您需要使用扩展来支持变量作为节点集。这将只加载文档一次。
    【解决方案3】:

    尝试将所有macro处理提取到单独的模板模式中,并且只对macro元素的内容运行它。即:

    <xsl:template match="macro">
       <xsl:apply-templates mode="macro"/>
    </xsl:template>
    
    <xsl:temlate match="text()[. = 'A1']" mode="macro">
       ...
    </xsl:template>
    

    我怀疑您的情况放缓是因为您的规则会针对输入中的每个节点逐一检查。这样,您可以检查它是否为 macro,并且只有当它是时,它的内容才会进一步匹配。

    【讨论】:

    • 这种方法的性能与我的第一种方法大致相同,但由于额外的&lt;xsl:apply-templates/&gt; 而略有损失。无论哪种方式,它实际上并不慢,或者至少不比 XSL 转换的预期慢。 libxslt 做得不错,我只是想看看是否可以从中挤出更多性能。
    猜你喜欢
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 2017-08-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 2016-11-04
    相关资源
    最近更新 更多