我发现自己使用了很多很多钥匙,有时我会在其中加上健全性检查,例如:
<xsl:key name="foo-with-bar" match="foo[contains(., 'bar')]">
<xsl:if test="@baz='xyz'">
<xsl:message terminate="yes">
Can't handle <foo> containing "bar" if @baz="xyz"
</xsl:message>
</xsl:if>
<xsl:value-of select="generate-id()"/>
</xsl:key>
(这实际上是一个非常简单的测试 - 实际的测试可能非常复杂。)这让我想到,为什么不在我实际上不需要密钥而只需要进行健全检查的情况下使用密钥? E.g:
<xsl:key name="sanity-check" match="foo[contains(., 'bar')][@baz='xyz']">
<xsl:message terminate="yes">
Can't handle <foo> containing "bar" if @baz="xyz"
</xsl:message>
</xsl:key>
我意识到除非我真正使用密钥,否则撒克逊不会终止,例如
<xsl:template match="/">
<xsl:apply-templates select="key('sanity-check', '')"/>
<xsl:copy-of select="."/>
</xsl:template>
但是,我能确定XSLT处理器实际上会以这种模式终止吗?我想这不是密钥的设计方式。
我意识到Schematron可以替代这种方法,但由于这些测试可能不是文档有效性,而是样式表是否能够处理文档,我发现样式表本身实现的测试非常有吸引力。 / p>
另一种选择可能是使用模板而不是键。我在这里看到两个选项:
使用模板进行单独的测试运行,如下所示:
<xsl:template match="/">
<xsl:apply-templates mode="sanity-check"/>
<xsl:apply-templates mode="actual-processing"/>
</xsl:template>
<xsl:template match="node()|@*" mode="sanity-check">
<xsl:apply-templates select="node()|@*" mode="sanity-check"/>
</xsl:template>
<xsl:template mode="sanity-check" match="foo[contains(., 'bar')][@baz='xyz']">
<xsl:message terminate="yes">
foo containing bar can't have @baz set to xyz
</xsl:message>
</xsl:template>
密钥仍然具有优势,因为无需额外的努力就可以实现无法以简单模式表达的更复杂的检查。使用模板,这将需要这样的结构:
<xsl:template mode="sanity-check" match="foo">
<xsl:variable name="variable" select="some-complicated-expression"/>
<xsl:if test="some-test-requiring[$variable=@bar]">
<xsl:message terminate="yes">
Fail!
</xsl:message>
</xsl:if>
<xsl:apply-templates mode="sanity-check" select="node()|@*"/>
</xsl:template>
这里有两个缺点:
<xsl:apply-templates mode="sanity-check" select="node()|@*"/>
包含更复杂的测试。match="A|B"
,另一个match="B|C"
,两者都需要一些<xsl:choose>
/ <xsl:if>
来决定是否需要终止。元素<B>
仅匹配其中一个模板。使用密钥,无需担心重叠匹配。