突发模式与完全流式 XSLT

Burst Mode Vs Fully Streaming XSLT

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

有人可以帮我理解 - 1. burst mode 和 Fully streaming 有什么区别? 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>

如果 JSON 中的属性顺序无关紧要,那么您可以使用 xsl:map/xsl:map-entry(或 XPath 3.1 map 直接创建 XSLT/XPath 3 个映射和数组] 构造函数)和 Saxon 特定扩展元素 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 有另一个捕获累加器的扩展来简化此类任务)。

到目前为止,我宁愿说更重要的是找到一种方法来绕过流式分析并使流式代码 return 与 non-streamable 代码得到正确且相同的结果;例如,在尝试使用两个 t运行sformation 步骤生成 JSON 的方法时,输入一些与您相似的样本数据,XML 表示为 JSON 第一个 t运行sformation 的结果和 JSON 应该是在第一步的结果上使用 xml-to-json 的结果我 运行 变成了 Saxon bug https://saxonica.plan.io/issues/4215.

对于流式传输,似乎没有足够的测试覆盖率或实现成熟度,无法以复杂且可扩展的方式可靠地组合功能,部分原因是规范复杂,部分原因是这些东西的使用有限XSLT 社区。

因此,如果您找到一种解决特定问题的工作方法,即使用流来保持内存消耗比正常的基于 XSLT 2/3 树的处理更低或可管理,那么您当然可以尝试进行更改以提高性能,但它很容易弄坏东西。

一个普遍的观察是,流式处理允许您访问当前 processed/matched 元素的所有属性,但不能访问其子元素,因此它可以极大地帮助插入处理步骤 t运行sforms 元素如果您有一个简单的子元素结构,则将其放入属性中。这样你就可以经常避免任何 copy-of()。但是当然,您需要一种方法来组合 Saxon 允许的两个样式表及其 API,但这样做需要编写 Java 或 .NET 代码。