源XML(这只是foobar数据,实际上它是数千行,可以是正面和负面):
<accounting>
<entry id="1">
<accounting_date>2010-10-29</accounting_date>
<transfer_date>2010-10-29</transfer_date>
<description>Start balance</description>
<vat>0</vat>
<sum>87287</sum>
</entry>
<entry id="2">
<accounting_date>2011-01-24</accounting_date>
<transfer_date>2011-02-17</transfer_date>
<description>Bill 1</description>
<vat>175</vat>
<sum>875</sum>
</entry>
<entry id="3">
<accounting_date>2011-01-31</accounting_date>
<transfer_date>2011-01-18</transfer_date>
<description>Bill 2</description>
<vat>350</vat>
<sum>1750</sum>
</entry>
</accounting>
我想将此XML转换为HTML表格以显示给用户。大多数转型只是将价值观放在正确的位置,但平衡场让我很头疼。
我的XSLT(不起作用):
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<xsl:for-each select="/accounting/entry">
<tr>
<td><xsl:value-of select="accounting_date" /></td>
<td><xsl:value-of select="description" /></td>
<td><xsl:value-of select="sum" /></td>
<td><xsl:value-of select="sum(../entry[position() < current()/position()]/sum)" /></td><!-- This XPath is the problem! -->
</tr>
</xsl:for-each>
</table>
预期结果:
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<tr>
<td>2010-10-29</td>
<td>Start balance</td>
<td>87287</td>
<td>87287</td>
</tr>
<tr>
<td>2011-01-24</td>
<td>Bill 1</td>
<td>875</td>
<td>88162</td>
</tr>
<tr>
<td>2011-01-31</td>
<td>Bill 2</td>
<td>1750</td>
<td>89912</td>
</tr>
</table>
Chrome是空白的,Firefox给了我:
Error loading stylesheet: XPath parse failure: Name or Nodetype test expected:
我被困住了,请帮忙。 :)
答案 0 :(得分:4)
最佳解决方案可能取决于您是使用XSLT 1.0还是XSLT 2.0。你真的需要说,因为目前在这个领域使用两者大致均匀。 (看来你在浏览器中运行它,这表明你想要一个1.0解决方案,所以这就是我给你的。)
但无论如何,递归是你的朋友。在这种情况下,“兄弟递归”,你写一个模板来处理一个条目,它确实应用模板来处理下一个条目,传递总数到目前为止参数:像这样
<xsl:template match="entry">
<xsl:param name="total-so-far" select="0"/>
<tr>
<td><xsl:value-of select="accounting_date" /></td>
<td><xsl:value-of select="description" /></td>
<td><xsl:value-of select="sum" /></td>
<td><xsl:value-of select="$total-so-far + sum"/></td><
</tr>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="total-so-far" select="$total-so-far + sum"/>
</xsl:apply-templates>
</xsl:template>
然后你需要用
开始这个过程<xsl:template match="accounting">
<table>
<xsl:apply-templates select="entry[1]"/>
</table>
</xsl:template>
如果有数千行,那么这可能导致XSLT处理器中的堆栈溢出,而不执行尾调用优化。我不知道今天的浏览器中的XSLT处理器是否实现了这种优化。
答案 1 :(得分:1)
或者,您可以使用前一个兄弟轴
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<xsl:for-each select="/accounting/entry">
<tr>
<td>
<xsl:value-of select="accounting_date" />
</td>
<td>
<xsl:value-of select="description" />
</td>
<td>
<xsl:value-of select="sum" />
</td>
<td>
<xsl:value-of select="sum(preceding-sibling::*/sum)+sum" />
</td>
</tr>
</xsl:for-each>
</table>
答案 2 :(得分:0)
除了@Michael Kay的正确答案之外,这里还有一个来自 FXSL 的通用模板/函数,用于计算运行总计。由于堆栈溢出,其DVC变体永远不会(出于实际目的)崩溃。使用DVC(Divide and Conquer)递归,处理1000000(1M)项的序列需要最大堆栈深度仅为19。
以下是使用scanl
模板的示例:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myAdd="f:myAdd"
xmlns:myParam="f:myParam"
>
<xsl:import href="scanlDVC.xsl"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<myAdd:myAdd/>
<myParam:myParam>0</myParam:myParam>
<xsl:template match="/">
<xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
<xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
<xsl:call-template name="scanl">
<xsl:with-param name="pFun" select="$vFun"/>
<xsl:with-param name="pQ0" select="$vZero" />
<xsl:with-param name="pList" select="/*/num"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="myAdd:*" mode="f:FXSL">
<xsl:param name="pArg1" select="0"/>
<xsl:param name="pArg2" select="0"/>
<xsl:value-of select="$pArg1 + $pArg2"/>
</xsl:template>
</xsl:stylesheet>
将此转换应用于以下XML文件:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
生成正确的结果(正在运行的总计):
<el>0</el>
<el>1</el>
<el>3</el>
<el>6</el>
<el>10</el>
<el>15</el>
<el>21</el>
<el>28</el>
<el>36</el>
<el>45</el>
<el>55</el>
将其用于提供的XML文档:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myAdd="f:myAdd"
xmlns:myParam="f:myParam"
>
<xsl:import href="scanlDVC.xsl"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<myAdd:myAdd/>
<myParam:myParam>0</myParam:myParam>
<xsl:template match="/">
<xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
<xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
<xsl:call-template name="scanl">
<xsl:with-param name="pFun" select="$vFun"/>
<xsl:with-param name="pQ0" select="$vZero" />
<xsl:with-param name="pList" select="/*/*/sum"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="myAdd:*" mode="f:FXSL">
<xsl:param name="pArg1" select="0"/>
<xsl:param name="pArg2" select="0"/>
<xsl:value-of select="$pArg1 + $pArg2"/>
</xsl:template>
</xsl:stylesheet>
并生成正确的结果::
<el>0</el>
<el>87287</el>
<el>88162</el>
<el>89912</el>