按Xsl中的子级值对xml节点进行分组

时间:2011-01-17 07:50:08

标签: xml xslt xpath

<root>
<element>
<id>1</id>
<group>first</group>
</element>

<element>
<id>2</id>
<group>second</group>
</element>


<element>
<id>3</id>
<group>first</group>
</element>
...
<root>

如何按xslt 1.0中的组名对元素进行分组。 输出:

<root>
<group name="first">
 <element>
    <id>1</id>
    <group>first</group>
 </element>
 <element>
    <id>3</id>
    <group>first</group>
 </element>
</group>
<group name="second">
 <element>
    <id>2</id>
    <group>second</group>
    </element>
</group>
</root>

有什么想法吗?

3 个答案:

答案 0 :(得分:15)

这是Muenchian Grouping的工作。您将在StackOverflow上的XSLT标记中找到许多示例。

首先,您需要定义一个键来帮助您对组进行分组

<xsl:key name="groups" match="group" use="."/>

这将查找给定组名的元素。

接下来,您需要匹配每个distince组名称的第一个实例的所有匹配项。这是通过这个可怕的陈述

来完成的
<xsl:apply-templates select="element/group[generate-id() = generate-id(key('groups', .)[1])]"/>

即匹配组元素恰好是我们键中该元素的第一个出现。

当您匹配不同的组节点时,您可以循环遍历具有相同名称的所有其他组节点(其中$ currentGroup是包含当前组名的变量)

<xsl:for-each select="key('groups', $currentGroup)">

完全放弃

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

   <xsl:key name="groups" match="group" use="."/>

   <xsl:template match="/root">
      <root>
         <xsl:apply-templates select="element/group[generate-id() = generate-id(key('groups', .)[1])]"/>
      </root>
   </xsl:template>

   <xsl:template match="group">
      <xsl:variable name="currentGroup" select="."/>
      <group>
         <xsl:attribute name="name">
            <xsl:value-of select="$currentGroup"/>
         </xsl:attribute>
         <xsl:for-each select="key('groups', $currentGroup)">
            <element>
               <id>
                  <xsl:value-of select="../id"/>
               </id>
               <name>
                  <xsl:value-of select="$currentGroup"/>
               </name>
            </element>
         </xsl:for-each>
      </group>
   </xsl:template>

</xsl:stylesheet>

将此应用于您的示例XML会产生以下结果

<root>
   <group name="first">
      <element>
         <id>1</id>
         <name>first</name>
      </element>
      <element>
         <id>3</id>
         <name>first</name>
      </element>
   </group>
   <group name="seccond">
      <element>
         <id>2</id>
         <name>seccond</name>
      </element>
   </group>
</root>

答案 1 :(得分:15)

<强>予。这是一个完整且非常简短的XSLT 1.0解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>


 <xsl:key name="kElsByGroup" match="element" use="group"/>

 <xsl:template match="/*">
  <root>
   <xsl:apply-templates/>
  </root>
 </xsl:template>

 <xsl:template match=
   "element[generate-id()=generate-id(key('kElsByGroup',group)[1])]">

  <group name="{group}">
   <xsl:copy-of select="key('kElsByGroup',group)"/>
  </group>
 </xsl:template>

 <xsl:template match=
   "element[not(generate-id()=generate-id(key('kElsByGroup',group)[1]))]"/>

</xsl:stylesheet>

将此转换应用于提供的XML文档

<root>
    <element>
        <id>1</id>
        <group>first</group>
    </element>
    <element>
        <id>2</id>
        <group>second</group>
    </element>
    <element>
        <id>3</id>
        <group>first</group>
    </element>
</root>

产生了想要的正确结果

<root>
    <group name="first"><element>
        <id>1</id>
        <group>first</group>
    </element><element>
        <id>3</id>
        <group>first</group>
    </element></group>
    <group name="second"><element>
        <id>2</id>
        <group>second</group>
    </element></group>
</root>

请注意

  1. 使用Muenchian method for grouping 。这是XSLT 1.0中最有效的分组方法。

  2. 使用AVT Attribute Value Template )将文字结果元素及其变量 - 值属性指定为一个整体。使用AVT可简化编码并产生更短且更易理解的代码。

  3. <强> II。更短的XSLT 2.0解决方案:

    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/*">
         <root>
          <xsl:for-each-group select="element" group-by="group">
           <group name="{current-grouping-key()}">
            <xsl:copy-of select="current-group()"/>
           </group>
          </xsl:for-each-group>
         </root>
     </xsl:template>
    </xsl:stylesheet>
    

    将此转换应用于同一XML文档(上图)时,会再次生成相同的正确结果。

    请注意

    0.1。 使用<xsl:for-each-group> XSLT 2.0指令。

    0.2。 使用标准XSLT 2.0函数current-group()current-grouping-key()

答案 2 :(得分:2)

<xsl:template match="group[not(.=preceding::group)]">
  <xsl:variable name="current-group" select="." />
  <xsl:for-each select="//root/element[group=$current-group]">
    <group>
      <id><xsl:value-of select="id"/></id>
    </group>
  </xsl:for-each>
</xsl:template>