Группировка узлов XML по значению дочернего элемента в Xsl - PullRequest
15 голосов
/ 17 января 2011
<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 ]

15 голосов
/ 17 января 2011

Это работа для мюнхенской группировки.Вы найдете многочисленные примеры этого в теге XSLT здесь, в StackOverflow.

Во-первых, вам нужно определить ключ, чтобы помочь вам сгруппировать группы

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

Это будет искать group элементы для данного имени группы.

Далее необходимо сопоставить все вхождения первого экземпляра каждого имени группы 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>
15 голосов
/ 17 января 2011

I.Вот полное и очень краткое решение 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. Использование мюнхенский метод группировки .Это наиболее эффективный метод группировки в XSLT 1.0.

  2. Использование AVT ( Шаблон значений атрибутов )указать буквальный элемент результата и его переменную - значение атрибута как одно целое.Использование AVT упрощает кодирование и дает более короткий и понятный код.

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-документу (см. Выше), снова получается тот же правильный результат.

Обратите внимание :

.1. Использование инструкции <xsl:for-each-group> XSLT 2.0.

.2. Использование стандартных функций XSLT 2.0 current-group() и current-grouping-key()

2 голосов
/ 17 января 2011
<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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...