突发模式与完全流式 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>}]} </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 代码。
我编写了一个 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>}]} </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 代码。