提问人:user2337472 提问时间:10/23/2023 最后编辑:user2337472 更新时间:10/25/2023 访问量:88
在流式处理时使用公共字段从循环外部的元素中提取数据
Pull data from an element outside of a loop using a common field while streaming
问:
我之前提出过类似的问题,但我的 XML 结构发生了变化/我需要利用流式处理,并且无法将其与我当前的解决方案一起使用。
我正在遍历 XML,但我需要引用的一些字段位于我的循环之外,并且依赖于当前循环的 ID,我可以使用一个通用的 ID 字段来匹配元素,但我需要它们在外部循环的上下文中匹配,而不是整个文档。我一直在尝试使用密钥,但无法让它在流式传输时工作/作为测试,我删除了流式引用,我在某种程度上得到了我需要的东西,但它与整个文档匹配,而不是我的循环。
<Date>13/10/2023 15/10/2023 20/10/2023</Date>
XML 结构
<Company><Employee_Stack_Grouped>
<Employee_Stack>
<Employee>
<Details>
<EmployeeID>ABC11111</EmployeeID>
</Details>
<Changes sequence="0">
<ChangeDetails>
<entryDate>20/10/2023</entryDate>
</ChangeDetails>
<Plans>
<Plan delete="Y">
<Name>Plan 1</Name>
<Start>01/01/2023</Start>
<ID>12345</ID>
</Plan>
<Plan updated="Y">
<Name>Plan 1</Name>
<Start>13/10/2023</Start>
<ID>12345</ID>
</Plan>
<Plan updated="Y">
<Name>Plan 2</Name>
<Start>13/10/2023</Start>
<ID>67890</ID>
</Plan>
<Plan>
<Name>Plan 3</Name>
<Start>01/01/2023</Start>
<ID>11111</ID>
</Plan>
<Interest_Plan updated="Y">
<Name>Plan 4</Name>
<Start>13/10/2023</Start>
<ID>22222</ID>
</Interest_Plan>
</Plans>
<Amounts>
<Earning updated="Y">
<Amount>1000</Amount>
<Plan>
<ID>12345</ID>
</Plan>
</Earning>
<Earning updated="Y">
<Amount>1000</Amount>
<Plan>
<ID>67890</ID>
</Plan>
</Earning>
<Earning>
<Amount>100</Amount>
<Plan>
<ID>11111</ID>
</Plan>
</Earning>
<Earning updated="Y">
<Amount>5000</Amount>
<Plan>
<ID>22222</ID>
</Plan>
</Earning>
</Amounts>
</Changes>
<Changes sequence="1">
<ChangeDetails>
<entryDate>23/10/2023</entryDate>
</ChangeDetails>
<Plans>
<Plan>
<Name>Plan 1</Name>
<Start>13/10/2023</Start>
<ID>12345</ID>
</Plan>
<Plan updated="Y">
<Name>Plan 2</Name>
<Start>15/10/2023</Start>
<ID>67890</ID>
</Plan>
<Plan>
<Name>Plan 3</Name>
<Start>01/01/2023</Start>
<ID>11111</ID>
</Plan>
<Interest_Plan>
<Name>Plan 4</Name>
<Start>13/10/2023</Start>
<ID>22222</ID>
</Interest_Plan>
</Plans>
<Amounts>
<Earning>
<Amount>1000</Amount>
<Plan>
<ID>12345</ID>
</Plan>
</Earning>
<Earning updated="Y">
<Amount>2500</Amount>
<Plan>
<ID>67890</ID>
</Plan>
</Earning>
<Earning>
<Amount>100</Amount>
<Plan>
<ID>11111</ID>
</Plan>
</Earning>
<Earning>
<Amount>5000</Amount>
<Plan>
<ID>22222</ID>
</Plan>
</Earning>
</Amounts>
</Changes>
</Employee>
<Employee>
<Details>
<EmployeeID>ABC222222</EmployeeID>
</Details>
<Changes sequence="0">
<ChangeDetails>
<entryDate>23/10/2023</entryDate>
</ChangeDetails>
<Plans>
<Plan updated="Y">
<Name>Plan 1</Name>
<Start>20/10/2023</Start>
<ID>12345</ID>
</Plan>
<Plan updated="Y">
<Name>Plan 2</Name>
<Start>20/10/2023</Start>
<ID>67890</ID>
</Plan>
<Plan>
<Name>Plan 3</Name>
<Start>01/01/2023</Start>
<ID>11111</ID>
</Plan>
<Interest_Plan updated="Y">
<Name>Plan 4</Name>
<Start>13/10/2023</Start>
<ID>22222</ID>
</Interest_Plan>
</Plans>
<Amounts>
<Earning updated="Y">
<Amount>1000</Amount>
<Plan>
<ID>12345</ID>
</Plan>
</Earning>
<Earning updated="Y">
<Amount>1000</Amount>
<Plan>
<ID>67890</ID>
</Plan>
</Earning>
<Earning>
<Amount>100</Amount>
<Plan>
<ID>11111</ID>
</Plan>
</Earning>
<Earning updated="Y">
<Amount>5000</Amount>
<Plan>
<ID>22222</ID>
</Plan>
</Earning>
</Amounts>
</Changes>
</Employee>
</Employee_Stack></Employee_Stack_Grouped></Company>
XSL 取消注释 Key 值会引发错误:提供的节点必须位于根为文档节点的树中。
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:mode streamable="true" on-no-match="shallow-skip"/>
<xsl:key name="plan" match="Plans/Plan[@updated = 'Y'] | Interest_Plan[@updated = 'Y']" use="ID"/>
<xsl:template match="/Company">
<File>
<xsl:for-each select="Employee_Stack_Grouped/Employee_Stack/Employee/copy-of()">
<xsl:variable name="employeeID" select="Details/EmployeeID"/>
<xsl:for-each select="Changes">
<xsl:variable name="entryDate" select="ChangeDetails/entryDate"/>
<xsl:for-each select="Amounts/Earning[@updated = 'Y']">
<row>
<Entry>
<xsl:value-of select="$entryDate"/>
</Entry>
<Employee_Number>
<xsl:value-of select="$employeeID"/>
</Employee_Number>
<Date>
<!-- <xsl:value-of select="key('plan', Plan/ID)/Start"/> -->
</Date>
<Amount>
<xsl:value-of select="Amount"/>
</Amount>
<ID>
<xsl:value-of select="Plan/ID"/>
</ID>
</row>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</File>
</xsl:template></xsl:stylesheet>
预期输出
<File>
<row>
<Entry>20/10/2023</Entry>
<Employee_Number>ABC11111</Employee_Number>
<Date>13/10/2023</Date>
<Amount>1000</Amount>
<ID>12345</ID>
</row>
<row>
<Entry>20/10/2023</Entry>
<Employee_Number>ABC11111</Employee_Number>
<Date>13/10/2023</Date>
<Amount>1000</Amount>
<ID>67890</ID>
</row>
<row>
<Entry>20/10/2023</Entry>
<Employee_Number>ABC11111</Employee_Number>
<Date>13/10/2023</Date>
<Amount>5000</Amount>
<ID>22222</ID>
</row>
<row>
<Entry>23/10/2023</Entry>
<Employee_Number>ABC11111</Employee_Number>
<Date>15/10/2023</Date>
<Amount>2500</Amount>
<ID>67890</ID>
</row>
<row>
<Entry>23/10/2023</Entry>
<Employee_Number>ABC222222</Employee_Number>
<Date>20/10/2023</Date>
<Amount>1000</Amount>
<ID>12345</ID>
</row>
<row>
<Entry>23/10/2023</Entry>
<Employee_Number>ABC222222</Employee_Number>
<Date>20/10/2023</Date>
<Amount>1000</Amount>
<ID>67890</ID>
</row>
<row>
<Entry>23/10/2023</Entry>
<Employee_Number>ABC222222</Employee_Number>
<Date>13/10/2023</Date>
<Amount>5000</Amount>
<ID>22222</ID>
</row></File>
请注意,可以有多个Employee_Stack_Grouped,然后是每个Employee_Stack多个员工,最后是每个员工的多个更改。我基本上需要输出任何带有更新标志的收益,但引用更改/计划元素中的日期。
我不确定是否可以在流式传输时使用密钥来实现此目的?由于我无法让它工作,我正在考虑做的另一个选择是在循环遍历 Changes 元素时,使用另一个循环将任何更新的计划存储在地图中,然后在我循环浏览收益时引用它。关于我如何实现这样的事情,有什么建议吗?
答:
使用捕获累积(目前是 Saxon 扩展,可能在 XSLT 4 中标准化),您可以使用例如
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:mode streamable="true" on-no-match="shallow-skip" use-accumulators="plans"/>
<xsl:accumulator name="plans" as="map(xs:integer, map(xs:string,xs:string))" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule phase="end" saxon:capture="yes" match="Plans/Plan[@updated = 'Y'] | Interest_Plan[@updated = 'Y']"
select="map:put($value, xs:integer(ID), map:merge(*!map:entry(local-name(), string())))"/>
</xsl:accumulator>
<xsl:template match="/Company">
<File>
<xsl:for-each select="Employee_Stack_Grouped/Employee_Stack/Employee/copy-of()">
<xsl:variable name="employeeID" select="Details/EmployeeID"/>
<xsl:for-each select="Changes">
<xsl:variable name="entryDate" select="ChangeDetails/entryDate"/>
<xsl:for-each select="Amounts/Earning[@updated = 'Y']">
<row>
<Entry>
<xsl:value-of select="$entryDate"/>
</Entry>
<Employee_Number>
<xsl:value-of select="$employeeID"/>
</Employee_Number>
<Date>
<xsl:value-of select="accumulator-before('plans')(xs:integer(Plan/ID))?Start"/>
</Date>
如果您没有“捕获”累加器的 Saxon 扩展,您仍然可以尝试将数据存储在多个累加器中,然后,您处理一个 ,创建映射条目:ID
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:mode streamable="true" on-no-match="shallow-skip" use-accumulators="#all"/>
<xsl:accumulator name="name" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="Plans/Plan[@updated = 'Y']/Name/text() | Interest_Plan[@updated = 'Y']/Name/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="start" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="Plans/Plan[@updated = 'Y']/Start/text() | Interest_Plan[@updated = 'Y']/Start/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="plans" as="map(xs:integer, map(xs:string,xs:string))" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Plans/Plan[@updated = 'Y']/ID/text() | Interest_Plan[@updated = 'Y']/ID/text()"
select="map:put($value, xs:integer(.), map { 'Start' : accumulator-before('start'), 'Name' : accumulator-before('name') })"/>
</xsl:accumulator>
<xsl:template match="/Company">
<File>
<xsl:for-each select="Employee_Stack_Grouped/Employee_Stack/Employee/copy-of()">
<xsl:variable name="employeeID" select="Details/EmployeeID"/>
<xsl:for-each select="Changes">
<xsl:variable name="entryDate" select="ChangeDetails/entryDate"/>
<xsl:for-each select="Amounts/Earning[@updated = 'Y']">
<row>
<Entry>
<xsl:value-of select="$entryDate"/>
</Entry>
<Employee_Number>
<xsl:value-of select="$employeeID"/>
</Employee_Number>
<Date>
<xsl:value-of select="accumulator-before('plans')(xs:integer(Plan/ID))?Start"/>
</Date>
你需要测试一下这种方法是否适用于9.7 EE,正如我所说,最终的XSLT 3版本是在该版本之后产生的,只有9.8及更高版本应该实现它。
评论
?
select="map:put($value, xs:integer(ID), map:merge(*!map:entry(local-name(), string())))"/>
Start
?Start
saxon:capture
下一个:XSLT 是否支持延迟计算?
评论