使用xsl:call-template优化转换模板(使用外部XML)

时间:2013-11-13 10:46:58

标签: xslt xslt-2.0

此时我已经有了一个XSLT(从现在起名为myLookupTable.xslt),它可以包含在其他XSLT文件中,以便能够进行一些代码转换。这个myLookupTable.xslt是一个命名模板,每个人都可以使用它来编写XSLT文件并想要使用它。所以我现在需要坚持这个xsl:call-template

XSLT目前工作正常,但是包含转换的非常大的XML文件花费了更多时间,我正在研究如何优化。

例如:

  • 使用xsl:key更好吗?如果是这样,我将如何在xsl:call-template
  • 中执行此操作
  • 任何其他结构都发生了变化,所以我仍然能够坚持xsl:call-template
  • 如果最好摆脱xsl:call-template,我为什么要这样做呢?我怎样才能让其他人尽可能轻松地实现这个呢?

我读过一篇关于Muenchian Method的文章,我明白了这一点,但不太确定如何在这个xsl:call-template示例中实现这一点。

感谢任何帮助和建议。

输入XML示例

<?xml version="1.0" encoding="UTF-8"?>
<pref:data xmlns:pref="http://example.org/uri/data">
    <pref:PackageGroup>
        <pref:sendPackage>BX</pref:sendPackage>
        <pref:sendRelation>66778899</pref:sendRelation>
    </pref:PackageGroup>
    <pref:TypeGroup>
        <pref:sendType>80</pref:sendType>
        <pref:sendRelation>88996677</pref:sendRelation>
    </pref:TypeGroup>
</pref:data>

用于在输入XML上进行转换的XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pref="http://example.org/uri/data">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <!-- Include lookup table XSLT and add variable to the lookup table xml -->
    <xsl:include href="myLookupTable.xslt"/>
    <xsl:variable name="myLookupTableFile">myLookupTable.xml</xsl:variable>

    <!-- Identity template -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <!-- Translate packageCode -->
    <xsl:template match="pref:sendPackage">
        <xsl:copy>
            <xsl:call-template name="myLookup">
                <xsl:with-param name="file"><xsl:value-of select="$myLookupTableFile" /></xsl:with-param>
                <xsl:with-param name="direction">in</xsl:with-param>
                <xsl:with-param name="function">filter</xsl:with-param>
                <xsl:with-param name="table">packageCode</xsl:with-param>
                <xsl:with-param name="relation"><xsl:value-of select="following-sibling::pref:sendRelation"/></xsl:with-param>
                <xsl:with-param name="value"><xsl:value-of select="."/></xsl:with-param>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>

    <!-- Translate type -->
    <xsl:template match="pref:sendType">
        <xsl:copy>
            <xsl:call-template name="myLookup">
                <xsl:with-param name="file"><xsl:value-of select="$myLookupTableFile" /></xsl:with-param>
                <xsl:with-param name="direction">in</xsl:with-param>
                <xsl:with-param name="function">filter</xsl:with-param>
                <xsl:with-param name="table">type</xsl:with-param>
                <xsl:with-param name="relation"><xsl:value-of select="following-sibling::pref:sendRelation"/></xsl:with-param>
                <xsl:with-param name="value"><xsl:value-of select="."/></xsl:with-param>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

包含xsl:call-template

的XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:conv="http://example.org/uri/lookuptable">
    <xsl:template name="myLookup">
        <xsl:param name="file"/><!-- Name of the conversion XML file -->
        <xsl:param name="direction"/><!-- 'in' ('directionIncoming' element is used) or 'out' ('directionOutgoing' element is used) -->
        <xsl:param name="function"/><!-- 'copy', 'copy+', 'filter', 'filter+' -->
        <xsl:param name="table"/><!-- Name of the lookup table to use for conversion (see 'name' attribute of 'translateCode' element) -->
        <xsl:param name="relation"/><!-- Relation number to use for conversion (see 'relation' attribute of 'translateValue' element) -->
        <xsl:param name="value"/><!-- Value to convert -->
        <xsl:variable name="fallbackRelation">0</xsl:variable>

        <!-- Step 1: convert input value -->
        <xsl:variable name="result1">
            <xsl:call-template name="convert">
                <xsl:with-param name="file" select="$file"/>
                <xsl:with-param name="direction" select="$direction"/>
                <xsl:with-param name="function" select="$function"/>
                <xsl:with-param name="table" select="$table"/>
                <xsl:with-param name="relation" select="$relation"/>
                <xsl:with-param name="value" select="$value"/>
            </xsl:call-template>
        </xsl:variable>

        <!-- Step 2: if result is empty and function name ends with '+' convert again using fallback relation number -->
        <xsl:variable name="result2">
            <xsl:choose>
                <xsl:when test="string-length($result1)=0 and ends-with($function,'+') and $relation!='0'">
                    <xsl:call-template name="convert">
                        <xsl:with-param name="file" select="$file"/>
                        <xsl:with-param name="direction" select="$direction"/>
                        <xsl:with-param name="function" select="$function"/>
                        <xsl:with-param name="table" select="$table"/>
                        <xsl:with-param name="relation" select="$fallbackRelation"/>
                        <xsl:with-param name="value" select="$value"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$result1"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <!-- Step 3: if result is still empty and function name starts with 'copy' use original input value as output result -->
        <xsl:variable name="result3">
            <xsl:choose>
                <xsl:when test="string-length($result2)=0 and starts-with($function,'copy')">
                    <xsl:value-of select="$value"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$result2"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <!-- Step 4: final conversion result -->
        <xsl:value-of select="$result3"/>
    </xsl:template>

    <!-- Template for actual conversion using external conversion XML file. -->
    <xsl:template name="convert">
        <xsl:param name="file"/>
        <xsl:param name="direction"/>
        <xsl:param name="function"/>
        <xsl:param name="table"/>
        <xsl:param name="relation"/>
        <xsl:param name="value"/>
        <xsl:variable name="result">
            <xsl:choose>
                <xsl:when test="$direction='in'">
                    <xsl:value-of select="document($file)/conv:myLookupTable/conv:table[@name=$table]/conv:directionIncoming/conv:translateCode[@name=$value]/conv:translateValue[@relation=$relation]/text()"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="document($file)/conv:myLookupTable/conv:table[@name=$table]/conv:directionOutgoing/conv:translateCode[@name=$value]/conv:translateValue[@relation=$relation]/text()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:value-of select="$result"/>
    </xsl:template>
</xsl:stylesheet>

可用于查找代码转换的XML

<?xml version="1.0" encoding="UTF-8"?>
<myLookupTable xmlns="http://example.org/uri/lookuptable">
    <table name="packageCode">
        <directionIncoming>
            <translateCode name="BX">
                <translateValue relation="99887766">GH</translateValue>
                <translateValue relation="66778899">LK</translateValue>
                <translateValue relation="88996677">LK</translateValue>
            </translateCode>
            <translateCode name="PL">
                <translateValue relation="99887766">BT</translateValue>
                <translateValue relation="66778899">LK</translateValue>
                <translateValue relation="88996677">LK</translateValue>
            </translateCode>
        </directionIncoming>
        <directionOutgoing>
            <translateCode name="LK">
                <translateValue relation="66778899">BX</translateValue>
                <translateValue relation="88996677">BX</translateValue>
            </translateCode>
            <translateCode name="BT">
                <translateValue relation="99887766">PL</translateValue>
            </translateCode>
            <translateCode name="GH">
                <translateValue relation="99887766">PL</translateValue>
            </translateCode>
        </directionOutgoing>
    </table>
    <table name="type">
        <directionIncoming>
            <translateCode name="10">
                <translateValue relation="99887766">20</translateValue>
                <translateValue relation="66778899">30</translateValue>
                <translateValue relation="88996677">30</translateValue>
            </translateCode>
            <translateCode name="80">
                <translateValue relation="99887766">90</translateValue>
                <translateValue relation="66778899">30</translateValue>
                <translateValue relation="88996677">30</translateValue>
            </translateCode>
        </directionIncoming>
        <directionOutgoing>
            <translateCode name="30">
                <translateValue relation="66778899">10</translateValue>
                <translateValue relation="88996677">10</translateValue>
            </translateCode>
            <translateCode name="90">
                <translateValue relation="99887766">80</translateValue>
            </translateCode>
            <translateCode name="20">
                <translateValue relation="99887766">80</translateValue>
            </translateCode>
        </directionOutgoing>
    </table>
</myLookupTable>

修改

当我按照下面的描述应用Martin Honnen他的解决方案时,我将输入XML从<pref:sendPackage>BX</pref:sendPackage>更改为<pref:sendPackage>XX</pref:sendPackage>(键查找将不返回任何内容)并且我在Altova XML Spy中执行转换我得到了错误:

Error from Altova XMLSpy

代码如下所示:

<xsl:value-of select="key('relationKey', $relation, key('incomingKey', $value, key('tableKey', $table, document($file))))/text()" />

即使我在它周围做xsl:if,测试也会通过(足够奇怪),但xsl:value-of select仍然会出现同样的错误:

<xsl:if test="normalize-space(key('k3', $relation, key('k2', $value, key('k1', $table, document($file))))/text()) != ''">
    <xsl:value-of select="key('k3', $relation, key('k2', $value, key('k1', $table, document($file))))/text()" />
</xsl:if>

1 个答案:

答案 0 :(得分:1)

您应该能够使用键来替换document($file)/conv:myLookupTable/conv:table[@name=$table]之类的表达式,例如

<xsl:key name="k1" match="conv:myLookupTable/conv:table" use="@name"/>

允许你重写

document($file)/conv:myLookupTable/conv:table[@name=$table]

as

key('k1', $table, document($file))

现在为conv:directionIncoming/conv:translateCode[@name=$value]定义

<xsl:key name="k2" match="conv:directionIncoming/conv:translateCode" use="@name"/>

然后替换

document($file)/conv:myLookupTable/conv:table[@name=$table]/conv:directionIncoming/conv:translateCode[@name=$value]

  key('k2', $value, key('k1', $table, document($file)))

最后是

document($file)/conv:myLookupTable/conv:table[@name=$table]/conv:directionIncoming/conv:translateCode[@name=$value]/conv:translateValue[@relation=$relation]

你会用

<xsl:key name="k3" match="conv:translateCode/conv:translateValue" use="@relation"/>

key('k3', $relation, key('k2', $value, key('k1', $table, document($file))))

我还建议尽可能使用<xsl:with-param name="param-name" select="foo"/>代替<xsl:with-param name="param-name"><xsl:value-of select="foo"/></xsl:with-param>,并使用<xsl:variable name="var-name" select="foo"/>代替<xsl:variable name="var-name"><xsl:value-of select="foo"/></xsl:variable>。在大多数情况下,这应该更有效。