Сортировка групп по свойству элемента arregate в xslt

Я преобразовываю некоторый xml с помощью xslt (версия 1.0, с использованием MSXSL).

Скажем, мои XML-данные выглядят так:

<table>
 <record><name>A</name><value>a</value><size>10</size></record>
 <record><name>A</name><value>b</value><size>35</size></record>
 <record><name>A</name><value>c</value><size>60</size></record>
 <record><name>B</name><value>x</value><size>15</size></record>
 <record><name>B</name><value>y</value><size>90</size></record>
 <record><name>B</name><value>z</value><size>20</size></record>
 ...
</table>

Моя цель:

  1. чтобы сгруппировать записи по <name>
  2. на группу, определите максимальное <size>, скажем maxsize
  3. сортировать группы по их maxsize (по убыванию)
  4. на группу, перечислить записи (в исходном порядке)

Итак, результат может быть таким:

<table>
 <group>B<maxsize>90</maxsize>
  <record><value>x</value><size>15</size>
  <record><value>y</value><size>90</size>
  <record><value>z</value><size>20</size>
 </group>
 <group>A<maxsize>60</maxsize>
  <record><value>a</value><size>10</size>
  <record><value>a</value><size>35</size>
  <record><value>a</value><size>60</size>
 </group>
</table>

Теперь шаги 1, 2 и 4, я могу это сделать. Но ... как я могу упорядочить группы по их максимальному размеру?

Я попытался создать новый набор узлов в переменной, содержащий группы. Я могу построить такой набор, но могу получить к нему доступ только как строку.

Должно быть возможно, правда?


person Michel de Ruiter    schedule 24.04.2013    source источник


Ответы (2)


Ну вот:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="kRecord" match="record" use="name"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates
        select="record[generate-id() = 
                       generate-id(key('kRecord', name)[1])]" 
        mode="group">
        <xsl:sort select="key('kRecord', name)/size
                              [not(. &lt; key('kRecord', ../name)/size)]" 
                  data-type="number"
                  order="descending" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="record" mode="group">
    <xsl:variable name="members" select="key('kRecord', name)" />
    <group>
      <xsl:value-of select="name" />
      <maxsize>
        <xsl:value-of select="$members/size[not(. &lt; $members/size)]"/>
      </maxsize>
      <xsl:apply-templates select="$members" />
    </group>
  </xsl:template>

  <xsl:template match="record/name" />
</xsl:stylesheet>

Когда вы запускаете свой образец ввода, результат будет следующим:

<table>
  <group>
    B<maxsize>90</maxsize><record>
      <value>x</value>
      <size>15</size>
    </record><record>
      <value>y</value>
      <size>90</size>
    </record><record>
      <value>z</value>
      <size>20</size>
    </record>
  </group>
  <group>
    A<maxsize>60</maxsize><record>
      <value>a</value>
      <size>10</size>
    </record><record>
      <value>b</value>
      <size>35</size>
    </record><record>
      <value>c</value>
      <size>60</size>
    </record>
  </group>
</table>

Между прочим, можно получить доступ к сконструированному набору узлов в переменной, если вы используете функцию node-set(), которая доступна в большинстве процессоров XSLT. Мне нравится избегать функции node-set(), когда это возможно, потому что она нестандартна и не имеет полной поддержки (а ее пространство имен даже не согласовано между процессорами, которые ее поддерживают), но вот как вы могли бы это сделать:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exslt="http://exslt.org/common" 
                exclude-result-prefixes="exslt">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="kRecord" match="record" use="name"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/table">
    <xsl:copy>
      <xsl:variable name="groups">
        <xsl:apply-templates
          select="record[generate-id() = 
                         generate-id(key('kRecord', name)[1])]"
          mode="group" />
      </xsl:variable>
      <xsl:apply-templates select="exslt:node-set($groups)/*">
        <xsl:sort select="maxsize" data-type="number" order="descending" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="record" mode="group">
    <xsl:variable name="members" select="key('kRecord', name)" />
    <group>
      <xsl:value-of select="name" />
      <maxsize>
        <xsl:apply-templates select="$members/size" mode="max">
          <xsl:sort select="." data-type="number" order="descending" />
        </xsl:apply-templates>
      </maxsize>
      <xsl:apply-templates select="$members" />
    </group>
  </xsl:template>

  <xsl:template match="record/name" />

  <xsl:template match="*" mode="max">
    <xsl:if test="position() = 1">
      <xsl:value-of select="." />
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
person JLRishe    schedule 24.04.2013

Это короткое преобразование (только два шаблона, без режимов, без переменных):

<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="kRecByName" match="record" use="name"/>

 <xsl:template match="/*">
  <table>
    <xsl:apply-templates select=
     "*[generate-id()=generate-id(key('kRecByName', name)
                                    [not(key('kRecByName', name)/size > size)][1])]">
      <xsl:sort select="size" data-type="number" order="descending"/>
    </xsl:apply-templates>
  </table>
 </xsl:template>

 <xsl:template match="record">
  <group><xsl:value-of select="name"/><maxsize><xsl:value-of select="size"/></maxsize>
    <xsl:copy-of select="key('kRecByName', name)"/>
  </group>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному XML-документу:

<table>
 <record><name>A</name><value>a</value><size>10</size></record>
 <record><name>A</name><value>b</value><size>35</size></record>
 <record><name>A</name><value>c</value><size>60</size></record>
 <record><name>B</name><value>x</value><size>15</size></record>
 <record><name>B</name><value>y</value><size>90</size></record>
 <record><name>B</name><value>z</value><size>20</size></record>
 ...
</table>

дает желаемый правильный результат:

<table>
   <group>B<maxsize>90</maxsize>
      <record>
         <name>B</name>
         <value>x</value>
         <size>15</size>
      </record>
      <record>
         <name>B</name>
         <value>y</value>
         <size>90</size>
      </record>
      <record>
         <name>B</name>
         <value>z</value>
         <size>20</size>
      </record>
   </group>
   <group>A<maxsize>60</maxsize>
      <record>
         <name>A</name>
         <value>a</value>
         <size>10</size>
      </record>
      <record>
         <name>A</name>
         <value>b</value>
         <size>35</size>
      </record>
      <record>
         <name>A</name>
         <value>c</value>
         <size>60</size>
      </record>
   </group>
</table>
person Dimitre Novatchev    schedule 25.04.2013