使用XSLT进行分组和计数

时间:2015-03-20 14:49:23

标签: xslt-1.0

我正在尝试使用XSLT 1.0评估,分组和计数节点,并且可以使用一些帮助。我需要做的是评估节点集并创建字符串,然后在输出任何内容之前对其进行分组和计数。

这是我的XML

<?xml version="1.0" encoding="UTF-8"?>
<DATA>
    <WIDGETS>
        <ITEM>
            <CODE>FX1</CODE>
        </ITEM>
        <ITEM>
            <CODE>SP2</CODE>
        </ITEM>
        <ITEM>
            <CODE>FX1</CODE>
        </ITEM>
        <ITEM>
            <CODE>P4</CODE>
        </ITEM>
        <ITEM>
            <CODE>WT</CODE>
        </ITEM>
        <ITEM>
            <CODE>XQ</CODE>
        </ITEM>
    </WIDGETS>
</DATA>

我的样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />      
<xsl:template match="DATA/WIDGETS">     
    <xsl:for-each select="ITEM">
        <xsl:choose> 
            <xsl:when test="CODE = 'FX1'">Performance Series </xsl:when>
            <xsl:when test="CODE = 'XQ'">Performance Series </xsl:when>
            <xsl:when test="CODE = 'SP2'">Sports Series </xsl:when>
            <xsl:when test="CODE = 'P4'">Sports Series </xsl:when>
            <xsl:when test="CODE = 'WT'">Classic Series </xsl:when>
        </xsl:choose>
    </xsl:for-each>
</xsl:template>     
</xsl:stylesheet>

当前输出如下所示

Performance Series Sports Series Performance Series Sports Series Classic Series Performance Series

我想要产生的是这个

Performance Series(x3) Sports Series(x2) Classic Series

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:2)

一种方法是存储第一个转换,在转换中将所有CODE转换为各自的系列,然后再处理变量以计算重复序列:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0">
    <xsl:output method="html" indent="yes" />
    <!-- key declaration to select NEWCODE using its value -->
    <xsl:key name="item" match="NEWCODE" use="."/>

    <xsl:template match="DATA/WIDGETS">
        <!-- variable TEMP, a temporary document containing NEWCODE 
            for every ITEM with respective series as its value -->
        <xsl:variable name="TEMP">
            <xsl:for-each select="ITEM">
                <NEWCODE>
                    <xsl:choose>
                        <xsl:when test="CODE = 'FX1'">Performance Series </xsl:when>
                        <xsl:when test="CODE = 'XQ'">Performance Series </xsl:when>
                        <xsl:when test="CODE = 'SP2'">Sports Series </xsl:when>
                        <xsl:when test="CODE = 'P4'">Sports Series </xsl:when>
                        <xsl:when test="CODE = 'WT'">Classic Series </xsl:when>
                    </xsl:choose>
                </NEWCODE>
            </xsl:for-each>
        </xsl:variable>

        <!-- iterating on every first NEWCODE(of its series) in variable TEMP, 
             and creating the desired string with count of its series --> 
        <xsl:for-each select="exslt:node-set($TEMP)/NEWCODE[count(. | key('item', .)[1]) = 1]">
            <xsl:value-of select="concat(., '(x', count(key('item', .)),') ')"/>
        </xsl:for-each>

    </xsl:template>
</xsl:stylesheet>

变量TEMP的处理是使用 Muenchian的分组完成的,该分组使用key()对元素进行分组。

在此回答中,文档顶部声明了xsl:key,稍后会在xsl:for-each中使用NEWCODE按其值选择xsl:for-each

NEWCODE首次出现xsl:for-each(首先是$ TEMP中的值)。在count(key('item', .))内,NEWCODE会计算所有NEWCODE,其值与您正在迭代的当前{{1}}相同。

答案 1 :(得分:1)

作为替代方案,您可以通过匹配每个类的第一个项目并将其转换为提供该类项目的总计数来执行此操作:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />

<xsl:template match="DATA/WIDGETS">     
  <xsl:apply-templates select="ITEM" />
</xsl:template>

<xsl:template match="ITEM"/>

<xsl:template match="ITEM[CODE = 'FX1' or CODE = 'XQ'][1]">
  <xsl:variable name="count" select="count(following-sibling::ITEM[CODE = 'FX1' or CODE = 'XQ']) + 1"/>
  <xsl:text>Performance Series</xsl:text>
  <xsl:if test="$count > 1">(x<xsl:value-of select="$count"/>)</xsl:if>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="ITEM[CODE = 'SP2' or CODE = 'P4'][1]">
  <xsl:variable name="count" select="count(following-sibling::ITEM[CODE = 'SP2' or CODE = 'P4']) + 1"/>
  <xsl:text>Sports Series</xsl:text>
  <xsl:if test="$count > 1">(x<xsl:value-of select="$count"/>)</xsl:if>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="ITEM[CODE = 'WT'][1]">
  <xsl:variable name="count" select="count(following-sibling::ITEM[CODE = 'WT']) + 1"/>
  <xsl:text>Classic Series</xsl:text>
  <xsl:if test="$count > 1">(x<xsl:value-of select="$count"/>)</xsl:if>
  <xsl:text> </xsl:text>
</xsl:template>

</xsl:stylesheet>

这确实会影响某些选择器的部分重复,但对我而言,这比其他答案更为惯用。它也避免了依赖扩展功能,但广泛支持。 YMMV。