当不同的文档组对相同的标签具有不同的可选标签属性时,如何最好地重用 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 样式表中,这些样式表负责转换属性 a 和 b,属性 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>
现在,让我们假设属性 a
和 b
都是可以用相同方式处理的通用属性,无论它们出现在什么元素上。 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>
这是场景。我有 XML 个带有如下标签的文档:
<para a="A" b="B" c="C">
出现在不同 class 的 XML 文档中。 a 和 b 属性是完全通用的,在所有文档中的处理方式完全相同。可选的 c 属性依赖于文档 class,并且需要不同的转换,具体取决于文档 class。我想编写一个样式表 included 或 imported 到文档 class-specific 样式表中,这些样式表负责转换属性 a 和 b,属性 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>
现在,让我们假设属性 a
和 b
都是可以用相同方式处理的通用属性,无论它们出现在什么元素上。 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>