XSLT:我可以创建一个自己的模板函数库吗?

时间:2015-09-15 12:17:27

标签: xml xslt xslt-2.0

我有几个XSLT转换。它们都包含相同的模板,看起来像这样(它的实现和功能无关紧要):

<xsl:template match="firstField| secondField | thirdField">
    <xsl:element name="{local-name(.)}">    
    <xsl:choose>
        <xsl:when test="string-length(.)!=0"><xsl:value-of select="."/></xsl:when>  
        <xsl:otherwise>ABSENT</xsl:otherwise>   
    </xsl:choose>   
    </xsl:element>
</xsl:template>

如您所见,我列出了要在模板匹配中应用此模板的字段。

但实际上我想在我的所有转换中使用此模板,但当然使用不同的字段名称。换句话说,我想使用它就像我可以插入任何.xsl文件的函数一样,并指定一个参数列表,这些是要以这种方式修改的字段的名称。

我可以使用XSLT吗?

3 个答案:

答案 0 :(得分:2)

更新,我可能误解了您的问题并重新审视了您的模板(有关解决方案说明的更一般说明,请参阅下文)。

您写道:

<xsl:template match="firstField| secondField | thirdField">
    <xsl:element name="{local-name(.)}">    
    <xsl:choose>
        <xsl:when test="string-length(.)!=0"><xsl:value-of select="."/></xsl:when>  
        <xsl:otherwise>ABSENT</xsl:otherwise>   
    </xsl:choose>   
    </xsl:element>
</xsl:template>

  

我想将它用作一个函数,我可以插入任何.xsl文件并指定一个参数列表,这些是要以这种方式修改的字段的名称。

如果用&#34;这种方式&#34;你的意思是说:

  • 将本地名称作为新元素名称(即,我假设您故意选择将其从命名空间中删除,否则xsl:copy就足够了)
  • 如果元素的内容为空,则输出&#34; ABSENT&#34;
  • 否则,取当前节点的值

然后你可以通过以下方式做到这一点。但是,只有当您的要求像您所说的那样一般适用于您的用例时,这才有效。

按如下方式编写模板:

<xsl:variable name="names">
    <names>
        <n>firstField</n>
        <n>secondField</n>
        <n>thirdField</n>
    </names>
</xsl:variable>

<xsl:template match="*" mode="by-name">
   <xsl:element name="{local-name(.)}">
      <xsl:apply-templates select="self::node()" mode="text" />
   </xsl:element>
</xsl:template>

<xsl:template match="node()" mode="by-name" />

<xsl:template match="*[text()]" mode="text">
    <xsl:copy />
</xsl:template>

<xsl:template match="*[not(text())]" mode="text">
    <xsl:text>ABSENT</xsl:text>
</xsl:template>

<xsl:template match="/">
    <xsl:apply-templates select="your/current/whatever" />
</xsl:template>

<xsl:template match="*">
    <xsl:for-each select="exslt:node-set($names)/names/n/text()">
        <xsl:apply-templates select="self::*[name() = .]" mode="by-name" />
    </xsl:for-each>
</xsl:template>

将上面的代码放在一个单独的文件中,除变量外,你应该把它放在那里,但是把它留空。使用xsl:import导入此文件,您现在唯一需要做的就是覆盖xsl:variable

XSLT 2.0和3.0中有更多通用和简单的方法,但此版本适用于XSLT 1.0。

免责声明:未经测试,可能包含错误,当然也可以根据您的需求进行调整;)

更好/通用方法

是的,你可以这样做。不同的XSLT版本具有不同的抽象级别:

XSLT 1.0

您可以创建命名模板(只需为其命名)。如果使用xsl:call-template调用命名模板,则当前上下文项将用作模板内的上下文项。这将使用模板匹配中的字段名称解决您的问题。

您可以将其放在一个单独的文件中,然后使用xsl:import将其导入,以便在必要时覆盖它,或者xsl:include,它不允许覆盖,如果是,则会引发错误命名冲突出现了。

XSLT 2.0

您可以在XSLT 2.0中创建一个可以作为任何其他函数调用的函数。函数可以包含您在上面显示的模板。

在XSLT 2.0中,您还可以使用import和include。

XSLT 3.0

您可以执行与以前版本相同的操作,但现在可以将它们放在(预编译)包中,这样可以更轻松地重用,重新分发和调用它们。

此外,XSLT 3.0在覆盖和接受/暴露已使用软件包的组件方面有一个大大改进的系统。

所有版本

您可能有一个当前使用xsl:apply-templates的地方。如果您想阻止重复声明匹配的xsl:template,可以通过创建通用来解决此问题:

<xsl:template match="node()" mode="special">
    <xsl:call-template name="yourNamedTemplate" />
</xsl:template>

然后&#34;叫&#34;使用:

<xsl:apply-templates select="firstField | secondField | thirdField" mode="special" />

提示

如果可重用性很重要,那么您的&#34;库样式表&#34; (在XSLT 3.0中,官方术语&#34;库包&#34;弹出),您应该在命名空间中创建模式的名称。实际上,可重用样式表中的任何命名组件(命名模板,模式,函数,累加器,键)都应位于其自己的命名空间中。这可以防止冲突,如果用户想要覆盖它们,则必须明确地这样做。

您可以创建&#34;继承链&#34;。如果A导入B导入C,那么最高优先级将给予A中的命名组件,然后是B,然后是C.对于冲突的匹配模板也是如此。这通常不允许在你的主样式表中(因此设置优先级),但是A可以具有与B相同的匹配模板,或者作为C.在这种情况下,A超过B超过C。

答案 1 :(得分:1)

  

指定参数列表,这些参数是要包含的字段的名称   以这种方式修改。

问题的问题的答案是否定的:您无法告诉模板将自身应用于参数中提供的节点列表。调用命名模板或函数时,上下文不会更改。

但是,一旦建立了上下文,可以调用命名模板(或XSLT 2.0中的函数) - 如Justin的答案所示。

答案 2 :(得分:0)

排序。

您可以做的是创建一个包含您想要重复使用的实现的命名模板,如下所示:

<xsl:template name="MyTemplate">
    <xsl:element name="{local-name(.)}">    
    <xsl:choose>
        <xsl:when test="string-length(.)!=0"><xsl:value-of select="."/></xsl:when>  
        <xsl:otherwise>ABSENT</xsl:otherwise>   
    </xsl:choose>   
    </xsl:element>
</xsl:template>

然后,您可以调用此模板来创建匹配不同元素的模板,而无需重复模板主体。

<xsl:template match="firstField | secondField | thirdField">
    <xsl:call-template name="MyTemplate" />
</xsl:template>

(这是未经测试的,因此语法可能有点偏离)