我正在努力将基于表的XML分组(在多个键上)到XSLT层次结构的概念
分组基于前四个元素,但如果在集合之间存在另一个元素,则分组必须中断。
源XML:
<RECORDS>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A1</F1>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A2</F1>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>195</E5>
<F1>A3</F1>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A4</F1>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A5</F1>
</RECORD>
<RECORD>
<E1>DONALD</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A6</F1>
</RECORD>
<RECORD>
<E1>DONALD</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F1>A7</F1>
</RECORD>
</RECORDS>
输出XML
<RECORDS>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F>
<F1>A1</F1>
<F1>A2</F1>
</F>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>195</E5>
<F>
<F1>A3</F1>
<F1>A4</F1>
</F>
</RECORD>
<RECORD>
<E1>MICKEY</E1> <!--Must break and not merge in first group -->
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F>
<F1>A5</F1>
</F>
</RECORD>
<RECORD>
<E1>DONALD</E1>
<E2>TEST</E2>
<E4>14</E4>
<E5>196</E5>
<F>
<F1>A6</F1>
<F1>A7</F1>
</F>
</RECORD>
</RECORDS>
这是我到目前为止提出的XSL ......
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="grouped" match="RECORD"
use="concat(E1, '+', E2, '+', E4 , '+', E5 )"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<RECORDS>
<xsl:apply-templates select=
"RECORD[generate-id()
=
generate-id(key('grouped',
concat(E1, '+', E2, '+', E4 , '+', E5 )
)
[1]
)
]
"/>
</RECORDS>
</xsl:template>
<xsl:template match="RECORD">
<RECORD>
<E1><xsl:value-of select="E1"/></E1>
<E2><xsl:value-of select="E2"/></E2>
<E4><xsl:value-of select="E4"/></E4>
<F>
<xsl:for select="F1">
<F1><xsl:value-of select="F1"/></F1>
</xsl:for>
</F>
</RECORD>
</xsl:template>
</xsl:stylesheet>
问题在于我无法为每个f1生成内部标记reapeating。另外我应该得到4套RECORDS,而不是我得到的3套。
<RECORDS>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<F></F>
</RECORD>
<RECORD>
<E1>MICKEY</E1>
<E2>TEST</E2>
<E4>14</E4>
<F></F>
</RECORD>
<RECORD>
<E1>DONALD</E1>
<E2>TEST</E2>
<E4>14</E4>
<F></F>
</RECORD>
</RECORDS>
答案 0 :(得分:5)
以下是使用密钥的解决方案。 更短(代码行减少28%,不需要水平滚动)。 更强大(有关详细信息,请参阅此答案的结尾)
它更通用,因为它甚至可以在我们想要分组的元素之间的情况下工作,还有其他元素必须被忽略(即preceding-sibling::*[1]
可能是我们想要的元素从分组中排除 - 在当前问题中 - 不是RECORD
元素:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kStartGroup" match="/*/*" use=
"generate-id(preceding-sibling::*
[not(concat(E1, '|', E2, '|', E4, '|', E5)
= concat(current()/E1, '|', current()/E2, '|', current()/E4, '|', current()/E5)
)
][1])"/>
<xsl:template match="*[not(concat(E1, '|', E2, '|', E4, '|', E5)
=
concat(preceding-sibling::*[1]/E1, '|',
preceding-sibling::*[1]/E2, '|',
preceding-sibling::*[1]/E4, '|',
preceding-sibling::*[1]/E5)
)]">
<xsl:copy>
<xsl:copy-of select="E1 | E2 | E4 | E5"/>
<F><xsl:copy-of select=
"key('kStartGroup', generate-id(preceding-sibling::*[1]))/F1"/></F>
</xsl:copy>
</xsl:template>
<xsl:template match="/*"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
稳健性/可扩展性
因为此转换不包含递归(对<xsl:apply-templates
的嵌套调用),所以当应用于大型XML文件时,它是健壮且可伸缩的。
另一方面,当转换应用于足够大的XML文档时,在另一个答案中提供了“兄弟姐妹递归”解决方案由于堆栈溢出而崩溃。在我的情况下,使用大约13 000(13,000行)的源XML文档观察到崩溃 - 这可能因可用的RAM,XSLT处理器等而异。
即使在非常大的XML文档上,当前的转换也能成功执行 - 例如拥有1 200 000(100万和20万行)。
答案 1 :(得分:3)
显然你想在XSLT 1.0中做相当于XSLT 2.0的group-adjacent
。这可以使用称为“兄弟递归”的技术来实现:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/RECORDS">
<xsl:copy>
<!-- start the first group -->
<xsl:apply-templates select="RECORD[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RECORD">
<xsl:variable name="key" select="concat(E1, '+', E2, '+', E4 , '+', E5)" />
<xsl:copy>
<xsl:copy-of select="E1 | E2 | E4 | E5"/>
<F>
<xsl:copy-of select="F1"/>
<!-- immediate sibling in the same group -->
<xsl:apply-templates select="following-sibling::RECORD[1][concat(E1, '+', E2, '+', E4 , '+', E5) = $key]" mode="collect"/>
</F>
</xsl:copy>
<!-- start the next group -->
<xsl:apply-templates select="following-sibling::RECORD[not(concat(E1, '+', E2, '+', E4 , '+', E5)=$key)][1]"/>
</xsl:template>
<xsl:template match="RECORD" mode="collect">
<xsl:variable name="key" select="concat(E1, '+', E2, '+', E4 , '+', E5)" />
<xsl:copy-of select="F1"/>
<!-- immediate sibling in the same group -->
<xsl:apply-templates select="following-sibling::RECORD[1][concat(E1, '+', E2, '+', E4 , '+', E5) = $key]" mode="collect" />
</xsl:template>
</xsl:stylesheet>