XSLT 1.0 对节点进行分组和计数

Posted

技术标签:

【中文标题】XSLT 1.0 对节点进行分组和计数【英文标题】:XSLT 1.0 group and count nodes 【发布时间】:2014-06-10 19:44:04 【问题描述】:

我有带有模块的 XML,里面有一些部分......

<?xml version="1.0"?>
<root>
 <modul id="1">
    <part id="01" part_number="AAA"/>
    <part id="02" part_number="AAA"/>
    <part id="03" part_number="AAA"/>
    <part id="04" part_number="BBB"/>
 </modul>
 <modul id="2">
    <part id="05" part_number="AAA"/>
    <part id="06" part_number="AAA"/>
    <part id="07" part_number="CCC"/>
    <part id="08" part_number="BBB"/>
 </modul>
</root>

我想在每个模块中按 part_number 分组并计算节点出现次数:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="keyPartNumber" match="part" use="@part_number"/>
<xsl:template match="/">
<xsl:for-each select="root/modul">
<modul name="@id">
<xsl:for-each select="part[generate-id() = generate-id(key('keyPartNumber',@part_number)[1])]">
  <xsl:sort select="@part_number" order="ascending" data-type="text"/>                        
  <node title="@part_number (quantity: count(key('keyPartNumber',@part_number)))" />
  </xsl:for-each>
</modul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

但结果我得到的不是我想要的:

<?xml version="1.0" encoding="UTF-8"?>
<modul name="1">
  <node title="AAA (quantity: 5)"/>
  <node title="BBB (quantity: 2)"/>
</modul>
<modul name="2">
  <node title="CCC (quantity: 1)"/>
</modul>

例外结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<modul name="1">
  <node title="AAA (quantity: 3)"/>
  <node title="BBB (quantity: 1)"/>
</modul>
<modul name="2">
  <node title="AAA (quantity: 2)"/>
  <node title="BBB (quantity: 1)"/>
  <node title="CCC (quantity: 1)"/>
</modul>

我怎样才能意识到这一点?是否可以为每个模块动态创建密钥?

【问题讨论】:

【参考方案1】:

这是因为您需要将 modul id 作为键的一部分包含在内,以便各个部分因模块而异

<xsl:key name="keyPartNumber" match="part" use="concat(../@id, '|', @part_number)"/>

注意这里的| 可以是任何东西,只要它不在 id 或 part_number 中即可。

您也将在访问密钥时使用相同的 concat 语句。

试试这个 XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="keyPartNumber" match="part" use="concat(../@id, '|', @part_number)"/>

    <xsl:template match="/">
        <xsl:for-each select="root/modul">
            <modul name="@id">
                <xsl:for-each select="part[generate-id() = generate-id(key('keyPartNumber',concat(../@id, '|', @part_number))[1])]">
                    <xsl:sort select="@part_number" order="ascending" data-type="text"/>                        
                    <node title="@part_number (quantity: count(key('keyPartNumber',concat(../@id, '|', @part_number))))" />
                </xsl:for-each>
            </modul>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

【讨论】:

【参考方案2】:

或者,您可以在“modul”中使用一个变量来存储它的@id,并使用它来过滤当前“root/modul”中的那些“部分”:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="keyPartNumber" match="part" use="@part_number"/>
<xsl:template match="/">
    <root>
        <xsl:for-each select="root/modul">
            <xsl:variable name="id" select="@id"/>
            <modul name="@id">
                <xsl:for-each select="part[generate-id() = generate-id(key('keyPartNumber',@part_number)[parent::*[@id = $id]][1])]">
                    <xsl:sort select="@part_number" order="ascending" data-type="text"/>                        
                    <node title="@part_number (quantity: count(key('keyPartNumber',@part_number)[parent::*[@id = $id]]))" />
                </xsl:for-each>
            </modul>
        </xsl:for-each>
    </root>
</xsl:template>
</xsl:stylesheet>

【讨论】:

以上是关于XSLT 1.0 对节点进行分组和计数的主要内容,如果未能解决你的问题,请参考以下文章

XSLT 1.0 - 连接已知子节点,按未知父节点分组

在 XSLT 中对每个父节点下的子节点进行分组

XSLT 1.0 将同一级别的多个相同节点以不同的值分组

XSLT muenchian 在子节点中按值分组

XSLT 1.0 中的分组和计数

如何在封闭元素下组织(分组)节点 - XSLT