【问题标题】:counting sum with XSLT from XML row that have same items使用 XSLT 从具有相同项目的 XML 行计算总和
【发布时间】:2016-05-10 09:22:27
【问题描述】:

对不起,我的英语不是母语,所以有些单词和术语可能是错误的。

我有很多项目的 XML。有些项目可能是很多次。我希望它是 hatml 表,但相同的项目只列出一次,并且总数已汇总。

普通的 for-each 很简单,但它只是列出了所有内容。如何为我的 XSLT 添加一些条件等?

这是我的示例 XML:

<Amounts>
    <item>
        <id>02</id>
        <name>Item2</name>
        <amount>20</amount>
    </item>
    <item>
        <id>01</id>
        <name>Item1</name>
        <amount>80</amount>
    </item>
    <item>
        <id>06</id>
        <name>Item6</name>
        <amount>50</amount>
    </item>
    <item>
        <id>02</id>
        <name>Item2</name>
        <amount>150</amount>
    </item>
</Amounts>

现在这是我的 XSLT:

<table>
<tr>
    <td>id</td>
    <td>name</td>
    <td>total</td>
</tr>
<xsl:for-each select="/Amounts/item">
<tr>
    <td><xsl:value-of select="id"/></td>
    <td><xsl:value-of select="name"/></td>
    <td><xsl:value-of select="amount"/></td>
</tr>
</table>

但我不希望这样:

<table>
<tr>
    <td>id</td>
    <td>name</td>
    <td>total</td>
</tr>
<tr>
    <td>01</td>
    <td>Item1</td>
    <td>80</td>
</tr>
<tr>
    <td>02</td>
    <td>Item2</td>
    <td>170</td>
</tr>
<tr>
    <td>06</td>
    <td>Item6</td>
    <td>50</td>
</tr>   
</table>

【问题讨论】:

标签: xml xslt


【解决方案1】:

使用 Muenchian grouping 您可以在 XSLT 1.0 中解决这个问题

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output method="html"/>

    <xsl:key name="group" match="item" use="id"/>

    <xsl:template match="Amounts">
        <table>
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Amount</th>
                </tr>
            </thead>
            <tbody>
                <xsl:apply-templates select="item[generate-id() = generate-id(key('group', id)[1])]">
                    <xsl:sort select="id" data-type="number"/>
                </xsl:apply-templates>
            </tbody>
        </table>
    </xsl:template>

    <xsl:template match="item">
        <tr>
            <td>
                <xsl:value-of select="id"/>
            </td>
            <td>
                <xsl:value-of select="name"/>
            </td>
            <td>
                <xsl:value-of select="sum(key('group', id)/amount)"/>
            </td>
        </tr>
    </xsl:template>

</xsl:stylesheet>

【讨论】:

    【解决方案2】:

    XSLT 1.0:解决方案的主要思想是在 id 属性值上使用一个键, 如https://stackoverflow.com/a/2293626/2156349 中所述。

    <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        version="1.0"
        >
        <xsl:output method="html" indent="yes" />       
        <xsl:strip-space elements="*"/>
    
        <xsl:key name="itemsKey" match="item/id/text()" use="." />
    
        <xsl:template match="/">
            <html>
            <head>  <title>Solution</title> </head>
            <body>  <xsl:apply-templates /> </body>
            </html>
        </xsl:template>
    
        <xsl:template match="Amounts">
            <table>
                <tr>
                    <td>id</td>
                    <td>name</td>
                    <td>total</td>
                </tr>
                <xsl:for-each select="item">
                    <xsl:variable name="idText" select="id/text()" />
                    <xsl:variable name="myId"   select="generate-id($idText)" />
                    <xsl:variable name="firstId" select="generate-id(key('itemsKey',$idText)[1])" />
    
                    <xsl:if test="$myId = $firstId">
                        <xsl:apply-templates select="." />
                    </xsl:if>
                </xsl:for-each>
            </table>
        </xsl:template>
    
        <xsl:template match="item">
            <xsl:variable name="idValue" select="id" />
            <tr>
                <td><xsl:value-of select="id"/></td>
                <td><xsl:value-of select="name"/></td>
                <td><xsl:value-of select="sum(//item[id=$idValue]/amount)"/></td>
            </tr>
        </xsl:template>
    
    </xsl:stylesheet>
    

    XSLT 2.0:使用上面 Martin Honnen 的提示,使用 XSLT 2.0 可以大大简化整个转换:

    <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        version="2.0"
        >
        <xsl:output method="html" indent="yes" />
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="/">
            <html>
            <head> <title>Solution</title> </head>
            <body> <xsl:apply-templates /> </body>
            </html>
        </xsl:template>
    
        <xsl:template match="Amounts">
            <table>
                <tr>
                    <td>id</td>
                    <td>name</td>
                    <td>total</td>
                </tr>
                <xsl:for-each-group select="item" group-by="id">
                    <tr>
                        <td><xsl:value-of select="id"/></td>
                        <td><xsl:value-of select="name"/></td>
                        <td><xsl:value-of select="sum(current-group()/amount)"/></td>
                    </tr>
                </xsl:for-each-group>
            </table>
        </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

    • 整个&lt;xsl:for-each select="item"&gt;...&lt;/xsl:for-each&gt;可以缩短为&lt;xsl:apply-templates select="item[generate-id() = generate-id(key('itemsKey', id)[1])]"/&gt;。并且定义了密钥后,在计算总和时应该使用它:&lt;xsl:value-of select="sum(key('itemsKey', id)/amount)"/&gt;。
    • 你是对的。我首先考虑使用apply-templates。但我认为for-each 变体更具可读性。但是,使用 sum(key('itemsKey', id)/amount) 不起作用。但是,我不明白为什么。
    • 对不起,马丁!看来,我们俩同时加了一个“孟池解”。我喜欢您最初在您的第一条评论中提出的(您的)Muenchian 解决方案(XSLT 2)。
    猜你喜欢
    • 1970-01-01
    • 2012-10-03
    • 2021-10-23
    • 2012-04-19
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    相关资源
    最近更新 更多