2015-01-26 120 views
1

我有非常大的輸入文檔(千紀錄)(數據代表了許多子元素):使用XSL:蓄電池以xsl:儘量/ XSL:具有結構像這樣抓

<Input> 
    <Record id="1"> 
    <Data/> 
    </Record> 
    <Record id="2"> 
    <Data/> 
    </Record> 
    <Record id="3"> 
    <Data/> 
    </Record> 
    <Record id="4"> 
    <Data/> 
    </Record> 
    <Record id="5"> 
    <Data/> 
    </Record> 
    <Record id="6"> 
    <!-- This is bad data --> 
    <BadData/> 
    </Record> 
    <Record id="7"> 
    <Data/> 
    </Record> 
    <Record id="8"> 
    <Data/> 
    </Record> 
    <Record id="9"> 
    <!-- Also bad data --> 
    <BadData/> 
    </Record> 
</Input> 

我m使用樣式表處理它,該樣式表對每個記錄執行復雜變換,這可能會遇到許多動態錯誤。在這個應用程序中,如果有幾條記錄有不好的數據,我寧願不要停止轉換,但我想知道有關錯誤,以便稍後解決。我使用一個xsl:儘量/ XSL:抓允許處理繼續:

<xsl:stylesheet 
    version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:err="http://www.w3.org/2005/xqt-errors" 
    exclude-result-prefixes="xs err"> 

    <xsl:output indent="yes"/> 

    <xsl:strip-space elements="*"/> 

    <xsl:template match="Input"> 
    <Output> 
     <xsl:apply-templates/> 
    </Output> 
    </xsl:template> 

    <xsl:template match="Record"> 
    <xsl:variable name="preprocessed" as="element(GoodData)?"> 
     <xsl:try> 
     <xsl:apply-templates mode="preprocess" select="."/> 
     <xsl:catch> 
      <xsl:message expand-text="yes">Couldn't create good data for {@id} Code: {$err:code} {$err:description}</xsl:message> 
     </xsl:catch> 
     </xsl:try> 
    </xsl:variable> 
    <!-- Do some more logic on the preprocessed record --> 
    <xsl:if test="$preprocessed"> 
     <NewRecord id="{@id}"> 
     <xsl:sequence select="$preprocessed"/> 
     </NewRecord> 
    </xsl:if> 
    </xsl:template> 



    <xsl:template mode="preprocess" match="Record"> 
    <!-- This represents a very complex transform with many potential dynamic errors --> 
    <xsl:variable name="source" as="element(Data)" select="*"/> 
    <xsl:if test="$source"> 
     <GoodData/> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

這工作得很好,但它是通過大輸入文檔挖找到失敗的幾個記錄的疼痛。我想要做的是使用xsl:result-document將無效的Record元素的源代碼寫入新的Input文檔。我想補充一個xsl:累加器是這樣的:

<xsl:accumulator name="failed-source" initial-value="()" as="element(Record)*"> 
    <xsl:accumulator-rule match="Record" phase="end"> 
    <xsl:sequence select="$value, .[false()(:test for failure:)]"/> 
    </xsl:accumulator-rule> 
</xsl:accumulator> 

<xsl:template match="Input"> 
    <Output> 
    <xsl:apply-templates/> 
    </Output> 
    <xsl:if test="accumulator-after('failed-source')"> 
    <xsl:result-document href="failed.input.xml"> 
     <Input> 
     <xsl:sequence select="accumulator-after('failed-source')"/> 
     </Input> 
    </xsl:result-document> 
    </xsl:if> 
</xsl:template> 

不過,我想不出在XSL謂語什麼:蓄電池規則應該是,或者如果它甚至有可能使用這個模式。可以在不重構樣式表的情況下創建單個結果文檔?

注意:我知道下面的解決方案,但它不是我的第一選擇,因爲它似乎可能有更高的內存要求,但可能不是這樣。我還可以將所有記錄寫入單個文件,但我認爲這很危險,因爲一個源文檔可能會產生數千個故障。

<xsl:template match="Input"> 
    <xsl:variable name="processed" as="document-node()"> 
    <xsl:document> 
     <xsl:apply-templates/> 
    </xsl:document> 
    </xsl:variable> 
    <xsl:if test="$processed/NewRecord"> 
    <Output> 
     <xsl:sequence select="$processed/NewRecord"/> 
    </Output> 
    </xsl:if> 
    <xsl:if test="$processed/Record"> 
    <xsl:result-document href="failed.input.xml"> 
     <Input> 
     <xsl:sequence select="$processed/Record"/> 
     </Input> 
    </xsl:result-document> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="Record"> 
    <xsl:variable name="preprocessed" as="element(GoodData)?"> 
    <xsl:try> 
     <xsl:apply-templates mode="preprocess" select="."/> 
     <xsl:catch> 
     <xsl:message expand-text="yes">Couldn't create good data for {@id} Code: {$err:code} {$err:description}</xsl:message> 
     </xsl:catch> 
    </xsl:try> 
    </xsl:variable> 
    <!-- Do some more logic on the preprocessed record --> 
    <xsl:choose> 
    <xsl:when test="$preprocessed"> 
     <NewRecord id="{@id}"> 
     <xsl:sequence select="$preprocessed"/> 
     </NewRecord> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:sequence select="."/> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

回答

2

這是一個有趣的方法。

累加器的值必須始終是輸入節點的純函數。沒有辦法從其他活動中獲取信息,例如節點的處理是否失敗。我不清楚您是否可以獨立於您在這些記錄上執行的處理來檢測「壞記錄」:如果可以,也就是說,如果您本質上是對輸入進行自定義驗證,那麼此模式可能會工作得相當好。 (但在這種情況下,我認爲你不會做try/catch,而是你的主要處理函數首先檢查累加器,看看數據是否有效。)

請注意,累加器的規格允許計算一個累加器訪問其他累加器,但目前Saxon尚未實現。

我認爲解決這個問題的更常見的方法可能是將成功處理的結果和處理不成功的報告寫入同一個結果樹,然後在隨後的轉換過程中將其拆分。不幸的是XSLT 3.0流媒體功能在多通道處理領域沒有任何東西可以提供。但是,對於拆分過程,xsl:fork很可能是合適的。

+0

我曾考慮過寫一個驗證函數,但它會複製大量的工作。源模式不是用戶友好的,所以在處理過程中捕獲錯誤更爲方便。例如。 「一個空的序列是不允許的,因爲變量$ year的值比」exists(A21/@ att1)「更容易處理。我是否正確地認爲上述的後一種方法比基於累加器的內存配置文件具有更高的內存配置? – nine9ths 2015-01-27 01:11:43

+0

蓄能器的實現還很早,所以我們還沒有做太多的分析。測量它並看看。我認爲我建議將成功和錯誤結果放在一個文件中,然後在後續階段分裂的方法可能會奏效。 – 2015-01-27 09:14:05

+0

經過測量,我們發現將輸出保存在一個變量中並進行拆分(詳見上一個示例),結果在運行結束時,內存使用量僅有大約10%的衝擊,因此我們將繼續進行。運行第二次轉換絕對是處理這個問題的一種萬無一失的方法,由於實現細節,它比我們希望爲以後的調試帶來的便利帶來的開銷要多一些。非常感謝您的幫助。 – nine9ths 2015-01-29 19:38:41