突发模式与完全流式XSLT

时间:2019-05-07 17:59:09

标签: streaming xslt-3.0

我已经编写了XSLT,可以使用突发模式流将巨大的传入XML文件转换为JSON。我是XSLT的新手,并且听说有一种更好的方式来完全流式传输XSLT代码,它比突发模式更有效,更快。

有人可以帮助我理解- 1.突发模式与完全流传输有什么区别? 2.如何将下面的XSLT代码转换为完全流式传输以提高性能?

下面是我的突发模式XSLT代码-

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect" exclude-result-prefixes="xs" version="3.0">
    <xsl:mode streamable="yes" on-no-match="shallow-skip"/>
    <xsl:output method="text" encoding="UTF-8" indent="no"/>
    <xsl:template match="wd:Report_Data">
        <xsl:iterate select="wd:Report_Entry/copy-of()">
            <!--Define Running Totals for Statistics -->
            <xsl:param name="TotalHeaderCount" select="0"/>
            <xsl:param name="TotalLinesCount" select="0"/>
            <!--Write Statistics -->
            <xsl:on-completion>
                <xsl:text>{"Stats": </xsl:text>
                <xsl:text>{"Total Header Count": </xsl:text>
                <xsl:value-of select="$TotalHeaderCount"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"Total Lines Count": </xsl:text>
                <xsl:value-of select="$TotalLinesCount"/>
                <xsl:text>}}</xsl:text>
            </xsl:on-completion>
            <!--Write Header Details -->
            <xsl:text>{"id": "</xsl:text>
            <xsl:value-of select="wd:id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"revenue_stream": "</xsl:text>
            <xsl:value-of select="wd:revenue_stream"/>
            <xsl:text>",</xsl:text>
            <!--Write Line Details -->
            <xsl:text>"lines": [  </xsl:text>
            <!-- Count the number of lines for an invoice -->
            <xsl:variable name="Linescount" select="wd:total_lines"/>
            <xsl:iterate select="wd:lines">
                <xsl:text>      {</xsl:text>
                <xsl:text>"sequence": </xsl:text>
                <xsl:value-of select="wd:sequence"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"sales_item_id": "</xsl:text>
                <xsl:value-of select="wd:sales_item_id"/>
                <xsl:text>",</xsl:text>
            </xsl:iterate>
            <xsl:text>}]}&#10;</xsl:text>
            <!--Store Running Totals -->
            <xsl:next-iteration>
                <xsl:with-param name="TotalHeaderCount" select="$TotalHeaderCount + 1"/>
                <xsl:with-param name="TotalLinesCount" select="$TotalLinesCount + $Linescount"/>                
            </xsl:next-iteration>
        </xsl:iterate>
    </xsl:template>
</xsl:stylesheet>

以下是示例XML-

<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect">
    <wd:Report_Entry>
        <wd:id>CUSTOMER_INVOICE-6-1</wd:id>
        <wd:revenue_stream>TESTA</wd:revenue_stream>        
        <wd:total_lines>1</wd:total_lines>
        <wd:lines>
            <wd:sequence>ab</wd:sequence>
            <wd:sales_item_id>Administrative Cost</wd:sales_item_id>            
        </wd:lines>
    </wd:Report_Entry>
    <wd:Report_Entry>
        <wd:id>CUSTOMER_INVOICE-6-10</wd:id>
        <wd:revenue_stream>TESTB</wd:revenue_stream>        
        <wd:total_lines>1</wd:total_lines>
        <wd:lines>
            <wd:sequence>ab</wd:sequence>
            <wd:sales_item_id>Data - Web Access</wd:sales_item_id>
        </wd:lines>
    </wd:Report_Entry>  
</wd:Report_Data>

1 个答案:

答案 0 :(得分:1)

如果JSON中属性的顺序无关紧要,则可以使用xsl:map/xsl:map-entry(或XPath 3.1 map构造函数)和Saxon特定扩展名直接创建XSLT / XPath 3映射和数组元素saxon:array(不幸的是,XSLT 3语言标准缺少创建数组的指令)。此外,您的大多数迭代参数似乎都可以轻松实现为累加器:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    extension-element-prefixes="saxon"
    xpath-default-namespace="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect"
    exclude-result-prefixes="#all"
    version="3.0">

    <xsl:output method="adaptive" indent="yes"/>

    <xsl:mode streamable="yes" use-accumulators="#all" on-no-match="shallow-skip"/>

    <xsl:accumulator name="header-count" as="xs:integer" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="Report_Entry" select="$value + 1"/>
    </xsl:accumulator>

    <xsl:accumulator name="lines-count" as="xs:integer" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="Report_Entry/total_lines/text()" select="$value + xs:integer(.)"/>
    </xsl:accumulator>

    <xsl:template match="Report_Data">
        <xsl:apply-templates/>
        <xsl:sequence
            select="map {
                     'Stats': map { 
                          'Total Header Count' : accumulator-after('header-count'),
                          'Total Lines Count' : accumulator-after('lines-count')
                        }
                    }"/>
    </xsl:template>

    <xsl:template match="Report_Entry">
        <xsl:map>
            <xsl:apply-templates/>
        </xsl:map>
    </xsl:template>

    <xsl:template match="Report_Entry/id | Report_Entry/revenue_stream | lines/sequence | lines/sales_item_id">
        <xsl:map-entry key="local-name()" select="string()"/>
    </xsl:template>

    <xsl:template match="Report_Entry/lines">
        <xsl:map-entry key="local-name()">
            <saxon:array>
                <xsl:apply-templates/>
            </saxon:array>
        </xsl:map-entry>
    </xsl:template>

</xsl:stylesheet>

该示例使用自适应输出方法,因为您的当前示例不会创建单个JSON对象,而我只是尝试创建与当前代码相同的输出; JSON输出方法将需要一个映射或数组作为主要序列结果。

代码可在oXygen中与流和Saxon EE 9.9.1.1一起使用,不幸的是9.8认为代码不可流。

对于一般规则,在流式传输时使用累加器和模板匹配可以实现的目标是有限的;如您所见,要对total_lines元素中的值求和的累加器需要在文本子项上匹配,以不消耗累加器中的元素(Saxon具有捕获累加器的另一扩展,以简化此类任务)。

到目前为止,我宁愿说找到一种方法来解决可流动性分析并使可流动的代码返回与不可流动的代码相同的正确结果是更为重要的;例如,在尝试使用两个转换步骤(其中一些样本数据是您的输入),通过流传输生成JSON的方法时,JSON的XML表示形式是第一次转换的结果,而JSON应该是使用{ {1}}在第一步的结果中,我遇到了Saxon错误https://saxonica.plan.io/issues/4215

使用流技术,似乎没有足够的测试覆盖范围或实现成熟度,无法以复杂且可扩展的方式可靠地组合功能,部分原因是规格复杂,部分原因是XSLT对该功能的使用有限社区。

因此,如果您找到了解决特定问题的可行方法,以使用流技术来使内存消耗与基于XSLT 2/3的常规树相比更小或可管理,那么您当然可以尝试进行更改以提高性能,但是这很容易打破事物。

一个普遍的观察是,流允许您访问当前已处理/匹配的元素的所有属性,但不能访问其子元素,因此,如果您具有简单的子元素结构,它可以极大地帮助您插入将元素转换为属性的处理步骤。这样,您通常可以避免使用任何xml-to-json。但是,当然,您需要一种将Saxon允许的两个样式表与其API结合在一起的方法,但是这样做需要编写Java或.NET代码。