2015-01-26 65 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