2010-09-23 30 views
4

从XSLT 2.0开始,据我所知(纠正我,如果我错了),在异常处理语言中没有本机机制。处理XSLT异常的技巧

我有一些样式表试图对指定的输入文档块进行一些处理,而其他所有内容都是不变的。在我开始为给定的块创建输出之前,我很难检测到罕见的特殊情况。这些非常罕见,当我遇到它们时,我想要做的就是取消这个块上的处理,并且不改变它。某种异常处理是按顺序进行的,但XSLT对此帮助不大。我不想在这里混入Java或其他语言。

我有一个可行的解决方案如下所述,但我想知道其他方法。你们都有更好的方式来做这样的事情吗?

下面是我谈论的那种场景的一个例子。这里是一个输入文件:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

我想查看每个表,并用'B'替换所有的单元格值'a'。但是,如果表中某处有'x',我想只修改表格而不修改。我知道在这种情况下,我可以在桌上做一个tr/td[.='x']测试来发现这种情况。但在实际情况中,提前测试条件并不容易。

下面是一些XSLT不占例外:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="table"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:apply-templates mode="inner"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="td"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:choose> 
       <xsl:when test=". = 'a'"> 
        <xsl:value-of select="'B'"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="."/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates mode="inner" select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

的输出是:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
</doc> 

它确实在第二个表中的换人,我不想。

我的当前的解决方案是要做到这一点:

  1. 散发出每个表成可变的,而不是直接进入输出
  2. 如果发生异常,发出<EXCEPTION/>标签
  3. 每个表进行处理后,查看<EXCEPTION/>标签的变量。
  4. 如果发生异常,请复制原始表格,否则复制变量的内容。

下面是修改后的代码:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="table"> 
     <xsl:variable name="result"> 
      <xsl:copy> 
       <xsl:copy-of select="@*"/> 
       <xsl:apply-templates mode="inner"/> 
      </xsl:copy> 
     </xsl:variable> 
     <xsl:choose> 
      <xsl:when test="$result//EXCEPTION"> 
       <xsl:copy-of select="."/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy-of select="$result"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

    <xsl:template mode="inner" match="td"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:choose> 
       <xsl:when test=". = 'a'"> 
        <xsl:value-of select="'B'"/> 
       </xsl:when> 
       <xsl:when test=". = 'x'"> 
        <EXCEPTION/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="."/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates mode="inner" select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

和正确的输出:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 
+0

这里没有例外的东西。这是关于副作用自由说明性范式的美妙之处。所有可能会陷入副作用的功能都有一个可怕的测试。在这里,您针对不同的元素模式有不同的过程。正如你所写的,只要有可能使用模式匹配,就去做吧。可能有些情况下,这是困难的或不是最佳的。然后,以临时结果树为基础的两步处理就是你的例子。 – 2010-09-23 23:25:43

+0

@ Steven-Ourada,@ Nick-Jones和@Alejandro:好问题(+1)。看到我的答案是一个*非常简单*的解决方案,不需要try/catch机制:)对于非常复杂的情况,例外可能真的很有用,您必须等待XSLT 3.0。 – 2010-09-25 17:20:49

回答

1

如果你能等待XSLT 2.1会有那么是try/catch语句表达

+1

我相信他们已经决定XSLT(和XPATH和XQuery)的下一个版本将是“3.0”http://www.w3.org/Bugs/Public/show_bug.cgi?id=9715 – 2010-09-23 23:22:24

+0

很好。我等不及了,但我可以稍后再切换。这也让我看到了撒克逊的扩展(不可否认,我应该早点完成)。我正在使用HE版本,但EE版本确实有类似的机制(http://www.saxonica.com/documentation/extensions/instructions/try-instr.html)。不值得为此升级,但很高兴知道。 – 2010-09-23 23:43:01

1

的例子提供的内容非常简单,不需要任何异常处理功能。

mode属性是你的朋友

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <xsl:output omit-xml-declaration="yes"/> 

<xsl:template match="node()|@*" mode="#default copy"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*" mode="#current"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="table[tr/td[.='x']]"> 
    <xsl:apply-templates select="." mode="copy"/> 
</xsl:template> 

<xsl:template match="td/text()[.='a']">B</xsl:template> 
</xsl:stylesheet> 

当这种转变是在所提供的XML文档应用:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

想要的,正确的结果产生

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

在例外的情况下真正需要(我们还需要看到一个很好的用例),他们将在XSLT 3.0标准。

+1

我相信你注意到我特别提到我的测试用例非常简单。对不起,我不能给你整个真实的情况;无论如何,所有的细节都会掩盖基本想法。你应该相信我,我相信异常会对我有用:-)。有时候,最好的解决方案并不是概念上最完美的解决方案,而是最简单的解决方案... – 2010-09-25 19:13:40

+0

@ Steven-Ourada:是的,我*有*相信你。让自己更有说服力并举出这样的例子会更好。 :) – 2010-09-25 20:00:31