Xml XSLT:将数字相加并多次打印小计
我是XSLT的初学者,我发现我不能仅仅将数字加在一个变量上,然后以任何方式更改它的值 我有一个XML文档,其中有一个数字列表,我需要将这些数字相加,直到元素与特定属性值匹配为止,然后打印该数字并将其重置为0,然后继续相加,直到我再次看到该特定属性为止 例如,我有以下XML:Xml XSLT:将数字相加并多次打印小计,xml,xslt,Xml,Xslt,我是XSLT的初学者,我发现我不能仅仅将数字加在一个变量上,然后以任何方式更改它的值 我有一个XML文档,其中有一个数字列表,我需要将这些数字相加,直到元素与特定属性值匹配为止,然后打印该数字并将其重置为0,然后继续相加,直到我再次看到该特定属性为止 例如,我有以下XML: <list> <entry> <field type="num" value="189.5" /> </entry> <entry> <fiel
<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>
我已经读到,我可以通过使用带条件的求和来实现这一点。我知道如何使用for each,并相对指向元素,我也能够通过简单地汇总type=num的所有元素来使用sum,但是如何只汇总第一个num,直到出现type=summary,然后只从最后一个type=summary到下一个
我希望这样:
<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>
感谢您的帮助。正如注释中所建议的分组的不同解决方案一样,您也可以使用匹配模式来获取总和:
<?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>
模板匹配字段[@type='num']打印值并添加新行,模板匹配条目[field[@type='summary']]使用该变量
<xsl:variable name="sumCount" select="count(preceding-sibling::entry[field[@type='summary']])"/>
更新:要更详细地解释其工作原理,请执行以下操作:在模板匹配条目[field[@type='summary']]中,变量sumCount统计具有summary类型字段的所有以前条目:
因此,当模板匹配第一个摘要字段时,sumCount的值为0,当匹配第二个摘要字段时,sumCount的值为1。
第二行使用sum函数
sum(
preceding-sibling::entry
[
count(preceding-sibling::entry[field[@type='summary']]) =
$sumCount
]
/field[@type='num']/@value
)
对所有先前条目的所有字段[@type='num']/@value进行求和,这些条目的summary类型的先前字段数量与summary类型的当前字段数量相同:
因此,当匹配第二个摘要时,只有值为9.5、10和11的num字段的值将被摘要,因为它们与当前摘要字段具有相同数量的先前摘要字段。
对于值为189.5和1.5的num字段
是0,因此这些字段在sum函数中被省略。这是作为注释建议的分组的不同解决方案-您也可以使用匹配模式来获取总和:
<?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>
模板匹配字段[@type='num']打印值并添加新行,模板匹配条目[field[@type='summary']]使用该变量
<xsl:variable name="sumCount" select="count(preceding-sibling::entry[field[@type='summary']])"/>
更新:要更详细地解释其工作原理,请执行以下操作:在模板匹配条目[field[@type='summary']]中,变量sumCount统计具有summary类型字段的所有以前条目:
因此,当模板匹配第一个摘要字段时,sumCount的值为0,当匹配第二个摘要字段时,sumCount的值为1。
第二行使用sum函数
sum(
preceding-sibling::entry
[
count(preceding-sibling::entry[field[@type='summary']]) =
$sumCount
]
/field[@type='num']/@value
)
对所有先前条目的所有字段[@type='num']/@value进行求和,这些条目的summary类型的先前字段数量与summary类型的当前字段数量相同:
因此,当匹配第二个摘要时,只有值为9.5、10和11的num字段的值将被摘要,因为它们与当前摘要字段具有相同数量的先前摘要字段。
对于值为189.5和1.5的num字段
是0,因此这些字段在sum函数中被省略。您需要对其进行修改。首先将密钥定义为:
然后使用:
#<xsl:value-of select="sum(key('numbers', generate-id())/field/@value)" />#
要对当前组中的数字求和。您需要更改。首先将密钥定义为:
然后使用:
#<xsl:value-of select="sum(key('numbers', generate-id())/field/@value)" />#
对该党来说太晚了,几乎和马提亚一样:
<?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>
其思想是将所有字段相加,这些字段前面的摘要字段数与实际摘要字段数相同 来不及参加聚会,几乎和马蒂亚斯一样:
<?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>
其思想是将所有字段相加,这些字段前面的摘要字段数与实际摘要字段数相同 I.这是一个简单的、仅向前的解决方案-请注意,没有使用反向轴,时间复杂度为ON,空间复杂度为O1
这可能是所有提供的解决方案中最简单、最快的:
根本不需要惊人的复杂性或分组
没有变量,没有键,也没有用于缓存键->值的空间,没有求和
<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文档上,并且使用未优化尾部递归的XSLT处理器时,会导致堆栈溢出,这是由于一个很长的数据链
下面是另一个转换,即使使用非常大的XML文档,也不会导致堆栈溢出。同样,没有反向轴,没有键,没有分组,没有条件指令,没有计数,没有
而且,最重要的是,与高效、基于密钥的Muenchian分组相比,当在一个有105000行的XML文档上运行时,这种转换只需要后者的61%的时间:
<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>
此外,此转换的速度可以加快到不到50%,也就是说,使其速度提高一倍以上
Muenchian分组转换所花费的时间,通过将每个元素名称替换为*
给我们大家一个教训:非关键解决方案有时可能比基于关键点的解决方案更有效。I.这里有一个简单的、仅向前的解决方案-请注意,没有使用反向轴,时间复杂度刚好为开,空间复杂度仅为O1
这可能是所有提供的解决方案中最简单、最快的:
根本不需要惊人的复杂性或分组
没有变量,没有键,也没有用于缓存键->值的空间,没有求和
<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文档上,并且使用未优化尾部递归的XSLT处理器时,会导致堆栈溢出,这是由于一个很长的数据链
下面是另一个转换,即使使用非常大的XML文档,也不会导致堆栈溢出。同样,没有反向轴,没有键,没有分组,没有条件指令,没有计数,没有
而且,最重要的是,与高效、基于密钥的Muenchian分组相比,当在一个有105000行的XML文档上运行时,这种转换只需要后者的61%的时间:
<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>
此外,通过将每个元素名称仅替换为一个元素名称,可以将此转换的速度提高到不到50%,即比Muenchian分组转换所用时间快一倍以上*
给我们大家一个教训:非关键解决方案有时比基于关键点的解决方案更有效。这是一个分组问题,请进行搜索。您使用的是哪个版本,XSLT1.0还是2.0?我使用的是版本1。0@NovumCoder,与您被告知的相反,这不是分组问题。事实上,在时间和空间上都存在一个非常简单和有效的解决方案。这是一个分组问题,需要进行搜索。您使用的是哪个版本,XSLT1.0还是2.0?我使用的是版本1。0@NovumCoder,与您被告知的相反,这不是分组问题。事实上,在时间和空间上都存在一个非常简单和有效的解决方案。效果很好。但老实说,我不知道为什么它会起作用。你能解释一下第二行的sum函数和你使用entry count赋值$sumCount的方法吗。它有什么作用?@NovumCoder很高兴我能提供帮助,我刚刚更新了我的答案,并做了一些更详细的解释。如果还有不清楚的地方,请告诉我。一般情况下,这是ONn^2。对于这种问题来说太复杂了。我用一个大约1000行的中等XML文档测试了两个解决方案——你的和我的——即使在这种温和的情况下,仅向前的解决方案运行速度也要快100倍。对于大约5000行的文档,仅向前的解决方案的速度要快4500倍。我使用MSXML4运行转换得到了这些结果。我相信@ NovumCoder可以考虑尝试唯一的解决方案:@ DimITReNOVATCHEV感谢测试所有提供的解决方案并共享结果。正如解释——我不打算在这里提供最好的解决方案,只有一个可能与michael.hor257k建议的分组不同。有时,我只是喜欢使用匹配模式解决XSLT问题,而不管可能存在的效率问题。虽然无法证实michael关于你的方法会破坏程序的说法。在所有程序中为我工作。在xsltransform.net上,我相信您的经验,并认为您和michael的解决方案效率更高,+1都更好。@matthias_h,欢迎您。我认为SO的主要价值在于让每个人都能学习和享受乐趣。我希望我的解决方案和评论能帮助我们今天做到这一点:效果很好。但老实说,我不知道为什么它会起作用。你能解释一下第二行的sum函数和你使用entry count赋值$sumCount的方法吗。它有什么作用?@NovumCoder很高兴我能提供帮助,我刚刚更新了我的答案,并做了一些更详细的解释。如果还有不清楚的地方,请告诉我。一般情况下,这是ONn^2。对于这种问题来说太复杂了。我用一个大约1000行的中等XML文档测试了两个解决方案——你的和我的——即使在这种温和的情况下,仅向前的解决方案运行速度也要快100倍。对于大约5000行的文档,仅向前的解决方案的速度要快4500倍。我使用MSXML4运行转换得到了这些结果。我相信@ NovumCoder可以考虑尝试唯一的解决方案:@ DimITReNOVATCHEV感谢测试所有提供的解决方案并共享结果。正如解释——我不打算在这里提供最好的解决方案,只有一个可能与michael.hor257k建议的分组不同。有时,我只是喜欢使用匹配模式解决XSLT问题,而不管可能存在的效率问题。虽然无法证实michael关于你的方法会破坏程序的说法。在所有程序中为我工作。在xsltransform.net上,我相信您的经验,并认为您的效率和mi
chael的解决方案更好,两者都是+1。@matthias_h,欢迎您。我认为SO的主要价值在于让每个人都能学习和享受乐趣。我希望我的解决方案和评论今天能帮助我们做到这一点:对于关心性能的每个人来说:对于一个大约1000行的XML文档,这个解决方案比更简单的、仅转发的解决方案慢3.5倍多。对于大约5000行的XML文档,此解决方案的速度比简单的、仅向前的解决方案慢5倍。通过使用特定处理器对其进行测试来对性能进行一般性假设是不明智的。使用Xalan和Saxon 6.5处理器,使用大约5000行的XML文档运行测试,我无法区分此方法与更简单、仅向前的解决方案之间的区别。在这两起案件中,死刑都是即时执行的。我没有工具来测量这两个处理器的运行时间。在运行libxslt时,我确实有这样的工具:但是我无法进行比较,因为更简单、仅向前的解决方案每次都会使处理器崩溃。michael.hor257k,Saxon有此命令行选项用于测量和报告执行时间:-t。MSXSL也有类似的选项。对于MSXML4和Saxon,我对这两个解决方案的性能得到了类似的结果。在不优化尾部递归的XSLT处理器上,仅向前转换可能会导致大型文档的堆栈溢出。这种转换的DVC分治变体在任何这样的处理器上都可以毫无问题地工作,保持了原始转换的简单性和效率。michael.hor257k,请参阅我答案中的更新。它提供的转换不会导致堆栈溢出,不会引用任何反向轴,也不会使用任何键,而且对于Saxon 6.5.3,使用答案中的键定义的Muenchian分组只需要花费105000 105000行XML文档的61%的时间。使用密钥并不总是最有效的解决方案!对于所有关心性能的人来说:对于大约1000行的XML文档,此解决方案比更简单、仅向前的解决方案慢3.5倍多。对于大约5000行的XML文档,此解决方案的速度比简单的、仅向前的解决方案慢5倍。通过使用特定处理器对其进行测试来对性能进行一般性假设是不明智的。使用Xalan和Saxon 6.5处理器,使用大约5000行的XML文档运行测试,我无法区分此方法与更简单、仅向前的解决方案之间的区别。在这两起案件中,死刑都是即时执行的。我没有工具来测量这两个处理器的运行时间。在运行libxslt时,我确实有这样的工具:但是我无法进行比较,因为更简单、仅向前的解决方案每次都会使处理器崩溃。michael.hor257k,Saxon有此命令行选项用于测量和报告执行时间:-t。MSXSL也有类似的选项。对于MSXML4和Saxon,我对这两个解决方案的性能得到了类似的结果。在不优化尾部递归的XSLT处理器上,仅向前转换可能会导致大型文档的堆栈溢出。这种转换的DVC分治变体在任何这样的处理器上都可以毫无问题地工作,保持了原始转换的简单性和效率。michael.hor257k,请参阅我答案中的更新。它提供的转换不会导致堆栈溢出,不会引用任何反向轴,也不会使用任何键,而且对于Saxon 6.5.3,使用答案中的键定义的Muenchian分组只需要花费105000 105000行XML文档的61%的时间。使用密钥并不总是最有效的解决方案!很好的解决方案。关于qAccum param的一个问题。我认为我们不能使用变量并不断增加它们。我尝试使用xsl:variable并阻止了我使用它。xsl:param和xsl:with-param有什么区别?@NovumCoder作为简短的解释-不能更改an的值,但an的值只是一个默认值,可以在调用模板时重写。这里,当匹配第一个num时,param最初的值为0。对于以下字段,使用当前值pAccum 0+189调用模板,对于下一个字段(即第一个摘要),使用191 189+1.5调用模板。更多详细信息,例如:@NovumCoder,从template1传递给template2的同一命名参数与传递给template1的对象的参数不同-它们恰好具有相同的名称。因此,不违反变量的不变性原则。顺便说一句,您已经接受了一个解决方案,与此解决方案或Muenchian分组解决方案相比,使用大型XML文档的速度可能要慢数百倍或数千倍,而Muenchian分组解决方案的效率仍然低于此解决方案。非常好的解决方案。关于qAccum param的一个问题。我认为我们不能使用变量并不断增加它们。我三
使用xsl:variable初始化并阻止我使用它。xsl:param和xsl:with-param有什么区别?@NovumCoder作为简短的解释-不能更改an的值,但an的值只是一个默认值,可以在调用模板时重写。这里,当匹配第一个num时,param最初的值为0。对于以下字段,使用当前值pAccum 0+189调用模板,对于下一个字段(即第一个摘要),使用191 189+1.5调用模板。更多详细信息,例如:@NovumCoder,从template1传递给template2的同一命名参数与传递给template1的对象的参数不同-它们恰好具有相同的名称。因此,不违反变量的不变性原则。顺便说一句,您已经接受了一种解决方案,这种解决方案在处理大型XML文档时可能比此解决方案或Muenchian分组解决方案慢数百倍或数千倍,而Muenchian分组解决方案的效率仍然低于此解决方案。
189.5
1.5
#191#
9.5
11
10
#30.5#
<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>