当不同的文档组对相同的标签具有不同的可选标签属性时,如何最好地重用 XSLT 代码?

How best to re-use XSLT code when different groups of documents have different optional tag attributes for the same tags?

这是场景。我有 XML 个带有如下标签的文档:

<para  a="A"  b="B"  c="C">

出现在不同 class 的 XML 文档中。 a 和 b 属性是完全通用的,在所有文档中的处理方式完全相同。可选的 c 属性依赖于文档 class,并且需要不同的转换,具体取决于文档 class。我想编写一个样式表 included 或 imported 到文档 class-specific 样式表中,这些样式表负责转换属性 ab,属性 c 由父样式表处理。我至少可以想到几种方法来做到这一点,但我想知道是否有一些规范的最佳方法。

让我们调用要共享的样式表st-generic.xsl。 st-generic.xsl 中的每个模板将被命名为:

<xsl:template match="para" name="generic-para">...</xsl:template>

文档 class 特定样式表然后将导入 st-generic.xsl(而不是包含,以设置优先级),并且将包含如下所示的模板:

<xsl:template match="para">
   <xsl:call-template name="generic-para"/>
   {other stuff}
   <xsl:apply-templates/>
</xsl:template>

这可能有效,但似乎有点不雅。例如,在大多数情况下,generic-para 模板就足够了,因此该模板同样需要包含一个

<xsl:apply-templates/>

模板正文中的节点。我想知道是否有更好的方法来做到这一点?

目前我能想到的最好的是:

将此包含在您的 st-generic.xsl 文件中:

<xsl:template match="para">
    { do para processing }
    <xsl:apply-templates select="para" mode="custom" />
    <xsl:apply-templates />
</xsl:template>
<xsl:template match="para" mode="custom" priority="-5" />

然后当你需要自定义行为时,你可以把它放在你的主模板中:

<xsl:template match="para" mode="custom">
    { do custom para processing }
</xsl:template>

这将在通用文件中的 { do para processing }<xsl:apply-templates /> 之间调用,因此您可以让 custom 模板专注于自定义行为。

没有看到你的更多代码和输入,我不明白你为什么要使用命名模板。

让我们考虑输入中的两个假设元素:

<para  a="A"  b="B"  c="C">paracontent</para>
<div  a="A"  b="B"  c="C">divcontent</div>

现在,让我们假设属性 ab 都是可以用相同方式处理的通用属性,无论它们出现在什么元素上。 c 以不止一种方式处理,具体取决于父元素。

当然有模板匹配para个元素

<xsl:template match="para">

而且我不明白您为什么需要命名模板来处理该元素的属性。为什么不对所有属性简单地 apply-templates

<xsl:template match="para">
   <xsl:apply-templates select="@*"/>
   <!--Do stuff other than processing attributes...-->
</xsl:template>

然后,其他模板(未命名的模板)将匹配两个通用属性:

<xsl:template match="@a">
  <!--Process attribute a, no matter the parent element-->
</xsl:template>

<xsl:template match="@b">
  <!--Process attribute b, no matter the parent element-->
</xsl:template>

甚至可能

<xsl:template match="@a|@b">
  <!--Process attributes a or b, no matter the parent element-->
</xsl:template>

而您将为属性编写单独的模板 c:

<xsl:template match="para/@c">
  <!--Process attribute c, if para is the parent-->
</xsl:template>

<xsl:template match="div/@c">
  <!--Process attribute c, if div is the parent-->
</xsl:template>

所有这些代码仍然在单独的模板中,可以模块化和导入或随意包含。

XSL 使用 元素提供了解决此问题的规范方法。

在此特定示例中,您可以让 st-generic.xsl 样式表导入 customizations.xsl 样式表这将包括可选的定制:

st-generic.xsl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:import href="customizations.xsl"/>
   <xsl:template match="para">
      <xsl:if test="@a and string(@a)">
         {do stuff}
      </xsl:if>
      <xsl:if test="@b and string(@b)">
         {do other stuff}
      </xsl:if>
      <xsl:apply-imports/>
      <xsl:apply-templates/>
   </xsl:template>
/xsl:stylesheet>

customizations.xsl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="para">
      <xsl:if test="@c and string(@c)">
         {do special stuff}
      </xsl:if>
   </xsl:template>
/xsl:stylesheet>

如果您还在 st-generic.xsl 样式表中命名模板,则提供了一个完全灵活的解决方案,然后允许您导入 st-generic.xsl 到另一个样式表中,根据需要调用命名模板或使用 ,如前一个示例中所用。

所以,我原来的答案被证明是行不通的,因为当没有匹配的模板时 的工作方式很不幸。 底线是它求助于默认模板,当您按照我在原始答案中的方式使用它时,最终会弄乱输出。

在思考如何解决这个问题时,我想出了一个我更喜欢的替代解决方案,即使用包含样式表中定义的 属性集 。例如,要处理原始问题中提出的案例,您可以这样做:

主样式表如下所示:

<xsl:include href="generic-attributes.xsl"/>

<xsl:template  match="para">
  <fo:block xsl:use-attribute-sets="para_default-attrs">
    <xsl:if test="@a and string(@a)">
     {do stuff}
    </xsl:if>
    <xsl:if test="@b and string(@b)">
     {do other stuff}
    </xsl:if>
     ~~ other stuff ~~
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

通用-attributes.xsl 文件将包含以下内容:

<xsl:attribute-set  name="para_default-attrs">
  <xsl:attribute name="a">A</xsl:attribute>
  <xsl:attribute name="b">B</xsl:attribute>
</xsl:attribute-set>