“滥用”<xsl:key>进行健全性检查

时间:2015-11-19 06:54:35

标签: xslt key xslt-2.0 sanity-check

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

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

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

<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可以替代这种方法,但由于这些测试可能不是文档有效性,而是样式表是否能够处理文档,我发现样式表本身实现的测试非常有吸引力。 / p>

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

  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>
    
  3. 密钥仍然具有优势,因为无需额外的努力就可以实现无法以简单模式表达的更复杂的检查。使用模板,这将需要这样的结构:

    <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>仅匹配其中一个模板。使用密钥,无需担心重叠匹配。

0 个答案:

没有答案