0

我发现自己使用了很多很多密钥,有时我会在其中进行健全性检查,例如:

<xsl:key name="foo-with-bar" match="foo[contains(., 'bar')]">
  <xsl:if test="@baz='xyz'">
    <xsl:message terminate="yes">
      Can't handle &lt;foo> containing "bar" if @baz="xyz"
    </xsl:message>
  </xsl:if>
  <xsl:value-of select="generate-id()"/>
</xsl:key>

(这实际上是一个非常简单的测试 - 实际测试可能非常复杂。)这让我想到,在我实际上不需要密钥而只需要健全性检查的情况下,为什么不使用密钥呢?例如:

<xsl:key name="sanity-check" match="foo[contains(., 'bar')][@baz='xyz']">
  <xsl:message terminate="yes">
    Can't handle &lt;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 处理器实际上会以这种模式终止吗?我想这不是钥匙的设计方式。

背景:吸引力较小的替代品(如果 tl;dr 则忽略)

我意识到 Schematron 可以替代这种方法,但由于这些测试可能不是关于文档有效性,而是关于样式表是否能够处理文档,我发现由样式表本身实现的测试非常有吸引力。

另一种选择可能是使用模板而不是键。我在这里看到两个选项:

  1. 在常规处理期间,测试和终止模板会取代“正常”模板。但是,在常规处理期间,并非所有需要测试的节点都必须由可以被否决的模板匹配处理。
  2. 使用模板运行单独的测试,如下所示:

    <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>

这里有两个缺点:

  1. 需要更多样板代码。如果我们在那里犯了错误,测试将无法正确执行,但我们不会注意到。我们需要:
    • 像模板一样的“身份转换”
    • <xsl:apply-templates mode="sanity-check" select="node()|@*"/>在每个包含更复杂测试的模板中。
  2. 重叠测试的问题,例如一个带有match="A|B",另一个带有match="B|C",都需要一些<xsl:choose>/<xsl:if>来决定我们是否需要终止。一个元素<B>只会匹配其中一个模板。使用键,无需担心重叠匹配。
4

0 回答 0