xslt - 如何在 XSLT 中进行循环分组
问题描述
我有以下 XML 数据:
<root>
<Group>
<Line PalletID="3019379466001">000001</Line>
<Line PalletID="3019379466002">000002</Line>
<Line PalletID="3019379466003">000003</Line>
<Line PalletID="3019379466004">000004</Line>
<Line PalletID="3019379466005">000005</Line>
<Line PalletID="3019379466006">000005</Line>
<Line PalletID="3019379466007">000007</Line>
<Line PalletID="3019379466008">000008</Line>
<Line PalletID="3019379466009">000008</Line>
<Line PalletID="3019379466010">000010</Line>
<Line PalletID="3019379466001">000003</Line>
<Line PalletID="3019379466002">000004</Line>
<Line PalletID="3019379466003">000005</Line>
<Line PalletID="3019379466006">000007</Line>
</Group>
</root>
如果他们有相同的@PalletID 或 Line,我想拥有相同的组。
预期结果:
<root>
<Group GroupNo="1">
<Line PalletID="3019379466001">000001</Line>
<Line PalletID="3019379466001">000003</Line>
<Line PalletID="3019379466003">000003</Line>
<Line PalletID="3019379466003">000005</Line>
<Line PalletID="3019379466005">000005</Line>
<Line PalletID="3019379466006">000005</Line>
<Line PalletID="3019379466006">000007</Line>
<Line PalletID="3019379466007">000007</Line>
</Group>
<Group GroupNo="2">
<Line PalletID="3019379466002">000002</Line>
<Line PalletID="3019379466002">000004</Line>
<Line PalletID="3019379466004">000004</Line>
</Group>
<Group GroupNo="3">
<Line PalletID="3019379466008">000008</Line>
<Line PalletID="3019379466009">000008</Line>
</Group>
<Group GroupNo="4">
<Line PalletID="3019379466010">000010</Line>
</Group>
</root>
我正在玩for-each-group 和重复模板,但甚至没有接近。
第二个数据示例:
<root>
<Group>
<Line PalletID="3019379466001">000001</Line>
<Line PalletID="3019379466002">000004</Line>
<Line PalletID="3019379466003">000008</Line>
<Line PalletID="3019379466004">000010</Line>
<Line PalletID="3019379466005">000017</Line>
<Line PalletID="3019379466006">000019</Line>
<Line PalletID="3019379466007">000020</Line>
<Line PalletID="3019379466008">000021</Line>
<Line PalletID="3019379466009">000026</Line>
<Line PalletID="3019379466010">000026</Line>
<Line PalletID="3019379466011">000028</Line>
<Line PalletID="3019379466012">000028</Line>
<Line PalletID="3019379466013">000028</Line>
</Group>
</root>
预期结果:三组,第 26 行、第 28 行和其余行。该解决方案应处理大约 100 行。
得到了那个例子错误:嵌套函数调用太多。可能是由于无限递归
更新:
我的环境是 Saxon-EE 9.6.0.7 数组是在 Saxon 9.7 中实现的 是否可以避免使用它?
解决方案
这是一种使用键和递归函数的方法,似乎可以提供正确的输出:
<?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:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:key name="pallet" match="Line" use="@PalletID"/>
<xsl:key name="value" match="Line" use="."/>
<xsl:function name="mf:build-group" as="array(element(Line))">
<xsl:param name="lines" as="element(Line)*"/>
<xsl:param name="result" as="array(element(Line))"/>
<xsl:variable name="start-line" select="head($lines)"/>
<xsl:choose>
<xsl:when test="$start-line">
<xsl:variable name="primary-group"
select="key('pallet', $start-line/@PalletID, root($start-line))"/>
<xsl:variable name="secondary-group"
select="key('value', $primary-group, root($primary-group[1])) except ($result?*, $primary-group)"/>
<xsl:variable name="head-result"
select="array:join(($result, mf:build-group($secondary-group, array {$primary-group})))"/>
<xsl:sequence select="$head-result"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="mf:build-groups" as="array(element(Line))*">
<xsl:param name="lines" as="element(Line)*"/>
<xsl:param name="result" as="array(element(Line))*"/>
<xsl:variable name="start-line" select="head($lines)"/>
<xsl:choose>
<xsl:when test="$start-line">
<xsl:variable name="head-result" select="mf:build-group($start-line, [])"/>
<xsl:sequence select="mf:build-groups(tail($lines) except ($result?*, $head-result?*), ($result, $head-result))"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="Group">
<xsl:iterate select="mf:build-groups(Line, ())">
<Group GroupNo="{position()}">
<xsl:copy-of select="?*"/>
</Group>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/93dFK9M
在https://xsltfiddle.liberty-development.net/93dFK9M/2中,我已将其中一个函数替换为xsl:iterate
并使用了更通用且希望具有描述性的键名:
<?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:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:key name="primary" match="Line" use="@PalletID"/>
<xsl:key name="secondary" match="Line" use="."/>
<xsl:function name="mf:build-group" as="array(element(Line))">
<xsl:param name="lines" as="element(Line)*"/>
<xsl:param name="result" as="array(element(Line))"/>
<xsl:variable name="start-line" select="head($lines)"/>
<xsl:choose>
<xsl:when test="not($start-line)">
<xsl:sequence select="$result"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable
name="primary-group"
select="key('primary', $start-line/@PalletID, root($start-line))"/>
<xsl:variable
name="secondary-group"
select="key('secondary', $primary-group, root($primary-group[1])) except ($result?*, $primary-group)"/>
<xsl:sequence
select="array:join(($result, mf:build-group($secondary-group, array {$primary-group})))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="Group">
<xsl:iterate select="Line">
<xsl:param name="groups" as="array(element(Line))*" select="()"/>
<xsl:on-completion>
<xsl:iterate select="$groups">
<Group GroupNo="{position()}">
<xsl:copy-of select="?*"/>
</Group>
</xsl:iterate>
</xsl:on-completion>
<xsl:choose>
<xsl:when test=". intersect $groups?*">
<xsl:next-iteration/>
</xsl:when>
<xsl:otherwise>
<xsl:next-iteration>
<xsl:with-param name="groups" select="$groups, mf:build-group(., [])"/>
</xsl:next-iteration>
</xsl:otherwise>
</xsl:choose>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
如果我正确理解要求,则对第二个建议的以下改编似乎可以处理第一个和第二个输入样本:
<?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:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:key name="primary" match="Line" use="@PalletID"/>
<xsl:key name="secondary" match="Line" use="."/>
<xsl:function name="mf:build-group" as="element(Line)*">
<xsl:param name="lines" as="element(Line)*"/>
<xsl:param name="result" as="element(Line)*"/>
<xsl:variable name="start-line" select="head($lines)"/>
<xsl:choose>
<xsl:when test="not($start-line)">
<xsl:sequence select="$result"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable
name="primary-group"
select="key('primary', $start-line/@PalletID, root($start-line))"/>
<xsl:variable name="current-result" select="$result | $primary-group"/>
<xsl:variable
name="secondary-group"
select="key('secondary', $primary-group, root($primary-group[1])) except $current-result"/>
<xsl:sequence
select="mf:build-group($secondary-group, $current-result)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="Group">
<xsl:iterate select="Line">
<xsl:param name="groups" as="array(element(Line))*" select="()"/>
<xsl:on-completion>
<xsl:iterate select="$groups">
<Group GroupNo="{position()}">
<xsl:copy-of select="?*"/>
</Group>
</xsl:iterate>
</xsl:on-completion>
<xsl:choose>
<xsl:when test=". intersect $groups?*">
<xsl:next-iteration/>
</xsl:when>
<xsl:otherwise>
<xsl:next-iteration>
<xsl:with-param name="groups" select="$groups, array { mf:build-group(., ()) }"/>
</xsl:next-iteration>
</xsl:otherwise>
</xsl:choose>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
推荐阅读
- plsql - 在预期以下情况之一时遇到符号“INTO”
- c# - 用于在.net中选择范围的正则表达式
- java - 如何将图像存储到适合文件中
- kubernetes-ingress - 私有子网中的 EKS,公共子网中的负载均衡器
- c++ - 有没有办法在 C++ 中有效地复制包装的 JNI 对象?
- mongodb - 打印 MongoDB 集合数据 - GoLang,结果不如预期
- chef-infra - 需要澄清有关节点管理、Chef 中的角色
- lua - 将表拆分为 9
- javascript - 如何创建购物车以在其中添加多个商品而不刷新页面
- http-status-codes - 更正不更新文档的 PUT/POST 方法的 HTTP 状态代码