使用 XSLT 进行分组和计数

Grouping and Counting with XSLT

我正在尝试使用 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

有人能帮忙吗?

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

<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 到 select NEWCODE 中使用它们的值。

xsl:for-each 迭代第一次出现的 NEWCODE(首先是它们在 $TEMP 中的值)。在 xsl:for-each 中,count(key('item', .)) 将计算所有 NEWCODE 的值与您正在迭代的当前 NEWCODE 相同。

作为替代方案,您可以通过匹配每个 class 的第一项并将其转换为提供 class:

的项目总数来实现此目的
<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.