我需要对数据进行分组,直到新的数据元素出现,然后重复逻辑。
我的输入XML:
<File>
<Detail>
<Data>1</Data>
<Value>4</Value>
</Detail>
<Detail>
<Data>1</Data>
<Value>7</Value>
</Detail>
<Detail>
<Data>2</Data>
<Value>4</Value>
</Detail>
<Detail>
<Data>1</Data>
<Value>5</Value>
</Detail>
<Detail>
<Data>1</Data>
<Value>1</Value>
</Detail>
<File>
结果输出XML应如下所示:
<File>
<Detail>
<Data>1</Data>
<Value>11</Value>
</Detail>
<Detail>
<Data>2</Data>
<Value>4</Value>
</Detail>
<Detail>
<Data>1</Data>
<Value>6</Value>
</Detail>
<File>
相应地添加值。如您所见,值为1的数据标记分别分组,因为它们由数据标记2分隔。
请帮忙。
答案 0 :(得分:1)
对于本质上是而循环的常见XSLT 1.0方法是使用尾递归。首先,将模板应用于每个组中的第一个 Detail
元素,然后每个元素将模板应用于下一个元素,等等。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:template match="/*">
<!-- apply templates to the _first_ Detail element in each group -->
<xsl:copy>
<xsl:apply-templates select="Detail[
not(Data = preceding-sibling::Detail[1]/Data)]" />
</xsl:copy>
</xsl:template>
<!-- Detail element that is not the _last_ one in a contiguous group -->
<xsl:template match="Detail[Data = following-sibling::Detail[1]/Data]">
<xsl:param name="runningTotal" select="0" />
<xsl:apply-templates select="following-sibling::Detail[1]">
<xsl:with-param name="runningTotal" select="$runningTotal + Value" />
</xsl:apply-templates>
</xsl:template>
<!-- Detail element that _is_ the last one in a contiguous group -->
<xsl:template match="Detail">
<xsl:param name="runningTotal" select="0" />
<Detail>
<xsl:copy-of select="Data" />
<Value><xsl:value-of select="$runningTotal + Value" /></Value>
</Detail>
</xsl:template>
</xsl:stylesheet>
这里的想法是,两个Detail
模板中的第一个将触发与其紧随其后的兄弟具有相同Data
值的元素,并将累积一个运行总计Value
传递下来。当我们到达组中的 last 元素时,第二个模板将输出总计Value
。 (对于单例组的情况,例如示例中的第二个元素,组中的第一个元素也是最后一个元素,因此我们直接转到第二个模板,其默认runningTotal
值为零)。
XSLT 2.0版本要简单得多,因为for-each-group
本身支持对连续运行进行分组:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each-group select="Detail" group-adjacent="Data">
<Detail>
<xsl:copy-of select="Data" />
<Value><xsl:value-of select="sum(current-group()/Value)" /></Value>
</Detail>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
这是你可以看到它的一种方式:
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="/">
<File>
<xsl:for-each select="File/Detail[not(Data=preceding-sibling::Detail[1]/Data)]">
<Detail>
<xsl:copy-of select="Data"/>
<xsl:variable name="start" select="count(preceding-sibling::Detail)" />
<xsl:variable name="end" select="count(following-sibling::Detail[not(Data=current()/Data)][1]/preceding-sibling::Detail)" />
<xsl:variable name="n">
<xsl:choose>
<xsl:when test="$end">
<xsl:value-of select="$end - $start - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="count(following-sibling::Detail)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<Value>
<xsl:value-of select="sum(Value | following-sibling::Detail[position() <= $n]/Value)"/>
</Value>
</Detail>
</xsl:for-each>
</File>
</xsl:template>
</xsl:stylesheet>