2010-09-23 86 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