我是XSLT的初学者,并且发现我不能只是将数字加到变量中并以任何方式更改其值。
我有一个XML文档,其中包含我需要添加的数字列表,直到元素与特定属性值匹配,然后打印该数字将其重置为0并继续累加其余部分,直到我再次看到该特定属性。 / p>
例如我有这个XML:
<list>
<entry>
<field type="num" value="189.5" />
</entry>
<entry>
<field type="num" value="1.5" />
</entry>
<entry>
<field type="summary" />
</entry>
<entry>
<field type="num" value="9.5" />
</entry>
<entry>
<field type="num" value="11" />
</entry>
<entry>
<field type="num" value="10" />
</entry>
<entry>
<field type="summary" />
</entry>
</list>
现在我希望我的XSLT打印出来:
189.5
1.5
#191#
9.5
11
10
#30.5#
我已经读过,我可以通过在条件下使用sum()来做到这一点。我知道如何使用for-each并相对指向元素,iam也可以通过简单地总结所有类型= num来使用sum(),但是如何仅将第一个num加起来直到type = summary出现,然后仅下一个sum从最后一个类型=摘要到下一个?
我希望这样的事情:
<xsl:for-each select="list/entry">
<xsl:if test="field[@type='summary']">
<!-- we are now at a type=summary element, now sum up -->
#<xsl:value-of select="sum(WHAT_TO_PUT_HERE?)" />#
</xsl:if>
<xsl:if test="field[@type='num']">
<xsl:value-of select="field/@value" />
</xsl:if>
</xsl:for-each>
感谢任何帮助。
答案 0 :(得分:6)
<强>予。这是一个简单的,仅向前的解决方案 - 请注意,没有使用反向轴,时间复杂度只是 O(N),空间复杂度只是 O (1)
这可能是所有解决方案中最简单,最快速的方法:
所有 ......
都不需要怪异的复杂性或分组没有变量,没有密钥(没有空间用于缓存key-&gt;值),没有sum()......
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*"><xsl:apply-templates select="*[1]"/></xsl:template>
<xsl:template match="entry[field/@type = 'num']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat(field/@value, '
')"/>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="entry[field/@type = 'summary']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat('#', $pAccum, '#
')"/>
<xsl:apply-templates select="following-sibling::entry[1]"/>
</xsl:template>
</xsl:stylesheet>
这是流式转换的一个示例 - 它不需要完整的XML文档树存在于内存中,并且可以用于处理无限长或无限长的文档。
在提供的源XML文档上应用转换:
<list>
<entry>
<field type="num" value="189.5" />
</entry>
<entry>
<field type="num" value="1.5" />
</entry>
<entry>
<field type="summary" />
</entry>
<entry>
<field type="num" value="9.5" />
</entry>
<entry>
<field type="num" value="11" />
</entry>
<entry>
<field type="num" value="10" />
</entry>
<entry>
<field type="summary" />
</entry>
</list>
产生了想要的正确结果:
189.5
1.5
#191#
9.5
11
10
#30.5#
<强> II。更新强>
当运行在足够大的XML文档上并且使用不优化尾递归的XSLT处理器时,上面的转换会导致堆栈溢出,这是由于<xsl:apply-templates>
的长链
下面是另一个转换,即使使用非常大的XML文档也不会导致堆栈溢出。同样,没有反向轴,没有键,没有“分组”,没有条件指令,没有count()
,没有<xsl:variable>
......
而且,最重要的是,与基于密钥的Muenchian分组“高效”相比,当在具有105000(105000)行的XML文档上运行时,此转换仅占后者的61%:< / p>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select=
"*[1] | entry[field/@type = 'summary']/following-sibling::*[1]"/>
</xsl:template>
<xsl:template match="entry[field/@type = 'num']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat(field/@value, '
')"/>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="entry[field/@type = 'summary']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat('#', $pAccum, '#
')"/>
</xsl:template>
</xsl:stylesheet>
此外,通过仅用*
替换每个元素名称,可以加快这种转换,使得Muenchian分组转换所花费的时间少于50%(即,使其快两倍以上)。
我们所有人都需要学习的课程:非键解决方案有时可能比基于密钥的解决方案更有效。
答案 1 :(得分:2)
正如作为评论建议的分组的不同解决方案 - 您也可以使用匹配模式来获得总和:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="field[@type='num']">
<xsl:value-of select="@value"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="entry[field[@type='summary']]">
<xsl:variable name="sumCount" select="count(preceding-sibling::entry[field[@type='summary']])"/>
<xsl:text>#</xsl:text>
<xsl:value-of select="sum(preceding-sibling::entry[count(preceding-sibling::entry[field[@type='summary']]) = $sumCount]/field[@type='num']/@value)"/>
<xsl:text>#
</xsl:text>
</xsl:template>
</xsl:transform>
当应用于输入XML时,会产生输出
189.5
1.5
#191#
9.5
11
10
#30.5#
模板匹配field[@type='num']
打印值并添加换行符,匹配entry[field[@type='summary']]
的模板使用变量
<xsl:variable name="sumCount" select="count(preceding-sibling::entry[field[@type='summary']])"/>
检查summary
类型的先前字段数。然后,只打印具有相同数量的前num
字段的summary
类型的所有条目值的总和:
<xsl:value-of select="sum(preceding-sibling::entry[
count(preceding-sibling::entry[field[@type='summary']]) = $sumCount
]/field[@type='num']/@value)"/>
更新要更详细地说明其工作方式如何:在匹配entry[field[@type='summary']]
的模板中,变量sumCount
计算所有以前具有{{1}类型字段的条目{1}}:
summary
因此,当模板与第一个count(preceding-sibling::entry[field[@type='summary']])
字段匹配时,summary
的值为sumCount
,当匹配第二个0
字段时,summary
为sumCount
。
第二行使用1
函数
sum
将所有前一个(前一个)条目的所有sum(
preceding-sibling::entry
[
count(preceding-sibling::entry[field[@type='summary']]) =
$sumCount
]
/field[@type='num']/@value
)
与field[@type='num']/@value
类型的当前字段具有相同数量的先前字段summary
相加:
summary
因此,当第二个count(preceding-sibling::entry[field[@type='summary']]) = $sumCount
匹配时,只有值为summary
,num
和9.5
的{{1}}字段的值将归纳为它们与当前10
字段具有相同数量的先前11
字段
对于值为summary
和summary
的<{1}}字段,
num
是189.5
,因此1.5
函数中省略了这些字段。
答案 2 :(得分:1)
您需要Muenchian grouping的变体。首先将键定义为:
<xsl:key name="numbers" match="entry[field/@type='num']" use="generate-id(following-sibling::entry[field/@type='summary'][1])" />
然后使用:
#<xsl:value-of select="sum(key('numbers', generate-id())/field/@value)" />#
将当前组中的数字相加。
答案 3 :(得分:0)
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="//field[@type='num']">
<xsl:value-of select="concat(@value,'
')"/>
</xsl:template>
<xsl:template match="//field[@type='summary']">
<xsl:variable name="prevSumCnt" select="count(preceding::field[@type='summary'])"/>
<xsl:variable name="sum" select="sum(preceding::field[count(preceding::field[@type='summary'])=$prevSumCnt]/@value)"/>
<xsl:value-of select="concat('#',$sum,'#
')"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:transform>
这个想法是对所有具有相同数量的汇总字段的字段进行求和,而不是实际的汇总字段......