Как сгруппировать родительский атрибут с помощью мюнхенского метода?

Я новичок в XSLT и меня смущает мюнхенский метод группировки. Вот мой XML-документ

<?xml   version='1.0'?> 
<?xml-stylesheet type="text/xsl"    href="test.xslt"?>
<catalog>
    <cd PurchaseDate="20000101">
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <quantity>20</quantity>
        <price>10.90</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Hide your heart</title>
        <artist>Bonnie Tyler</artist>
        <country>UK</country>
        <quantity>10</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000102">
        <title>Greatest Hits</title>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <quantity>15</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Still got the blues</title>
        <artist>Gary Moore</artist>
        <country>UK</country>
        <quantity>5</quantity>
        <price>10.20</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>Eros</title>
        <artist>Eros Ramazzotti</artist>
        <country>EU</country>
        <quantity>6</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>One night only</title>
        <artist>Bee Gees</artist>
        <country>UK</country>
        <quantity>16</quantity>
        <price>10.90</price>
    </cd>
    <cd PurchaseDate="20000102">
        <title>Sylvias Mother</title>
        <artist>Dr.Hook</artist>
        <country>UK</country>
        <quantity>3</quantity>
        <price>8.10</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Maggie May</title>
        <artist>Rod Stewart</artist>
        <country>UK</country>
        <quantity>8</quantity>
        <price>8.50</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>Romanza</title>
        <artist>Andrea Bocelli</artist>
        <country>EU</country>
        <quantity>30</quantity>
        <price>10.80</price>
    </cd>
</catalog>

И XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes" />
    <xsl:key name="kByCountry" match="cd" use="country" />
    <xsl:output method="html" />
    <xsl:template match="catalog">
        <html>
            <body>
                <table border="1">
                    <xsl:for-each select="cd[count(.|key('kByCountry',country)[1]) = 1]">
                        <xsl:sort select="country" />
                        <tr bgcolor="#9acd32">
                            <td colspan="4">Country:<xsl:value-of select="country" /></td>
                        </tr>
                        <tr>
                            <td>Purchase Date</td>
                            <td>Quantity</td>
                            <td>Unit Price</td>
                            <td>Total</td>
                        </tr>
                        <tr>
                            <td>?date?</td>
                            <td><xsl:value-of select="quantity" />  </td>
                            <td><xsl:value-of select="price" /></td>
                            <td><xsl:value-of select="price*quantity" /></td>
                        </tr>
                        <tr>
                            <td colspan="3" align="right">Sub-total</td>
                            <td>?how to count subtotal together?</td>
                        </tr>
                    </xsl:for-each>
                    <tr>
                        <td colspan="3" align="right">Grand-total</td>
                        <td>?how to count all subtotal together?</td>
                    </tr>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

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


person Chen    schedule 27.04.2013    source источник


Ответы (2)


Хотя вы не показали того, что ожидали увидеть, вот решение, которое, я думаю, дает вам то, что вы хотите. Обратите внимание, что <xsl:for-each> не требуется; это решение на основе <xsl:template> немного более гибкое.

Когда этот XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="kCdByCountry" match="cd" use="country"/>

  <xsl:template match="/*">
    <html>
      <body>
        <table border="1">
          <xsl:apply-templates
             select="cd[generate-id() =                        
                        generate-id(key('kCdByCountry', country)[1])]">
            <xsl:sort select="country"/>
          </xsl:apply-templates>
          <tr bgcolor="#9acd32">
            <td colspan="4">
              <xsl:text>Total: </xsl:text>
              <xsl:call-template name="sumProducts">
                <xsl:with-param name="pElemList" select="/*/*"/>
              </xsl:call-template>
            </td>
          </tr>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="cd">
    <tr bgcolor="#9acd32">
      <td colspan="4">
        <xsl:text>Country: </xsl:text>
        <xsl:value-of select="country"/>
      </td>
    </tr>
    <tr>
      <td>Purchase Date</td>
      <td>Quantity</td>
      <td>Unit Price</td>
      <td>Total</td>
    </tr>
    <xsl:apply-templates select="key('kCdByCountry', country)" mode="values">
      <xsl:sort select="@PurchaseDate" data-type="number"/>
    </xsl:apply-templates>
    <tr bgcolor="#9acd32">
      <td colspan="4">
        <xsl:text>Subtotal: </xsl:text>
        <xsl:call-template name="sumProducts">
          <xsl:with-param
            name="pElemList"
            select="key('kCdByCountry', country)"/>
        </xsl:call-template>
      </td>
    </tr>
  </xsl:template>

  <xsl:template match="cd" mode="values">
    <tr>
      <td>
        <xsl:value-of select="@PurchaseDate"/>
      </td>
      <td>
        <xsl:value-of select="quantity"/>
      </td>
      <td>
        <xsl:value-of select="price"/>
      </td>
      <td>
        <xsl:value-of select="quantity * price"/>
      </td>
    </tr>
  </xsl:template>

  <xsl:template name="sumProducts">
    <xsl:param name="pElemList"/>
    <xsl:param name="pTotal" select="0"/>
    <xsl:choose>
      <xsl:when test="$pElemList">
        <xsl:variable name="vCurrentElem" select="$pElemList[1]"/>
        <xsl:call-template name="sumProducts">
          <xsl:with-param
            name="pElemList"
            select="$pElemList[position() &gt; 1]"/>
          <xsl:with-param
            name="pTotal"
            select="$pTotal + $vCurrentElem/price * $vCurrentElem/quantity"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$pTotal"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

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

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <cd PurchaseDate="20000101">
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <quantity>20</quantity>
    <price>10.90</price>
  </cd>
  <cd PurchaseDate="20000101">
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <quantity>10</quantity>
    <price>9.90</price>
  </cd>
  <cd PurchaseDate="20000102">
    <title>Greatest Hits</title>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <quantity>15</quantity>
    <price>9.90</price>
  </cd>
  <cd PurchaseDate="20000101">
    <title>Still got the blues</title>
    <artist>Gary Moore</artist>
    <country>UK</country>
    <quantity>5</quantity>
    <price>10.20</price>
  </cd>
  <cd PurchaseDate="20000103">
    <title>Eros</title>
    <artist>Eros Ramazzotti</artist>
    <country>EU</country>
    <quantity>6</quantity>
    <price>9.90</price>
  </cd>
  <cd PurchaseDate="20000103">
    <title>One night only</title>
    <artist>Bee Gees</artist>
    <country>UK</country>
    <quantity>16</quantity>
    <price>10.90</price>
  </cd>
  <cd PurchaseDate="20000102">
    <title>Sylvias Mother</title>
    <artist>Dr.Hook</artist>
    <country>UK</country>
    <quantity>3</quantity>
    <price>8.10</price>
  </cd>
  <cd PurchaseDate="20000101">
    <title>Maggie May</title>
    <artist>Rod Stewart</artist>
    <country>UK</country>
    <quantity>8</quantity>
    <price>8.50</price>
  </cd>
  <cd PurchaseDate="20000103">
    <title>Romanza</title>
    <artist>Andrea Bocelli</artist>
    <country>EU</country>
    <quantity>30</quantity>
    <price>10.80</price>
  </cd>
</catalog>

..выдается (желаемый?) результат:

<html>
  <body>
    <table border="1">
      <tr bgcolor="#9acd32">
        <td colspan="4">Country: EU</td>
      </tr>
      <tr>
        <td>Purchase Date</td>
        <td>Quantity</td>
        <td>Unit Price</td>
        <td>Total</td>
      </tr>
      <tr>
        <td>20000103</td>
        <td>6</td>
        <td>9.90</td>
        <td>59.4</td>
      </tr>
      <tr>
        <td>20000103</td>
        <td>30</td>
        <td>10.80</td>
        <td>324</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Subtotal: 383.4</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Country: UK</td>
      </tr>
      <tr>
        <td>Purchase Date</td>
        <td>Quantity</td>
        <td>Unit Price</td>
        <td>Total</td>
      </tr>
      <tr>
        <td>20000101</td>
        <td>10</td>
        <td>9.90</td>
        <td>99</td>
      </tr>
      <tr>
        <td>20000101</td>
        <td>5</td>
        <td>10.20</td>
        <td>51</td>
      </tr>
      <tr>
        <td>20000101</td>
        <td>8</td>
        <td>8.50</td>
        <td>68</td>
      </tr>
      <tr>
        <td>20000102</td>
        <td>3</td>
        <td>8.10</td>
        <td>24.3</td>
      </tr>
      <tr>
        <td>20000103</td>
        <td>16</td>
        <td>10.90</td>
        <td>174.4</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Subtotal: 416.7</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Country: USA</td>
      </tr>
      <tr>
        <td>Purchase Date</td>
        <td>Quantity</td>
        <td>Unit Price</td>
        <td>Total</td>
      </tr>
      <tr>
        <td>20000101</td>
        <td>20</td>
        <td>10.90</td>
        <td>218</td>
      </tr>
      <tr>
        <td>20000102</td>
        <td>15</td>
        <td>9.90</td>
        <td>148.5</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Subtotal: 366.5</td>
      </tr>
      <tr bgcolor="#9acd32">
        <td colspan="4">Total: 1166.6</td>
      </tr>
    </table>
  </body>
</html>

...который при отображении в формате HTML выглядит следующим образом:

введите здесь описание изображения

Секрет этого решения — рекурсивный именованный шаблон, который суммирует произведения каждой комбинации <quantity> и <price>. Этот шаблон используется для подсчета промежуточного итога по каждой стране и, в конце концов, общего количества всех стран. Особая благодарность Дмитрию Новачеву за этот драгоценный камень (Умножить 2 числа, а затем суммировать< /а>).

person ABach    schedule 27.04.2013
comment
!Это поразительно! На самом деле в моем XML-файле много элементов cd. Так что возможно ли снова сгруппировать одну и ту же дату в таблице? - person Chen; 27.04.2013
comment
Эй, ABach, что заставило вас удалить этот текст из исходного ответа: (спасибо Димитре Новатчеву за красиво собранный шаблон): умножьте 2 числа, а затем просуммируйте с помощью XSLT). ? :) - person Dimitre Novatchev; 27.04.2013
comment
Упс! Долгая неделя и поздняя ночь; извините, @DimitreNovatchev! Я положу это обратно. - person ABach; 27.04.2013
comment
@Chen - я не понимаю, что ты имеешь в виду. Не могли бы вы обновить свой вопрос с требуемым выводом XML? - person ABach; 27.04.2013

Это преобразование:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kCDPurchByCountryDate" match="cd"
  use="concat(@PurchaseDate,'+', country)"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <xsl:apply-templates/>
  </xsl:variable>

  <xsl:apply-templates select="ext:node-set($vrtfPass1)/*">
   <xsl:sort select="@country"/>
   <xsl:sort select="@PurchaseDate" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match=
 "cd[generate-id()
    =generate-id(key('kCDPurchByCountryDate',
                     concat(@PurchaseDate,'+', country)
                     )[1]
                 )]">
  <trans country="{country}" PurchaseDate="{@PurchaseDate}">
   <amount><xsl:value-of select="quantity*price"/></amount>
   <xsl:apply-templates mode="group" select=
    "key('kCDPurchByCountryDate',concat(@PurchaseDate,'+', country))
        [position() > 1]
    "/>
  </trans>
 </xsl:template>

 <xsl:template match="cd" mode="group">
   <amount><xsl:value-of select="quantity*price"/></amount>
 </xsl:template>
 <xsl:template match="text()"/>

 <xsl:template match="trans">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <total><xsl:value-of select="sum(amount)"/></total>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

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

<catalog>
    <cd PurchaseDate="20000101">
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <quantity>20</quantity>
        <price>10.90</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Hide your heart</title>
        <artist>Bonnie Tyler</artist>
        <country>UK</country>
        <quantity>10</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000102">
        <title>Greatest Hits</title>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <quantity>15</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Still got the blues</title>
        <artist>Gary Moore</artist>
        <country>UK</country>
        <quantity>5</quantity>
        <price>10.20</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>Eros</title>
        <artist>Eros Ramazzotti</artist>
        <country>EU</country>
        <quantity>6</quantity>
        <price>9.90</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>One night only</title>
        <artist>Bee Gees</artist>
        <country>UK</country>
        <quantity>16</quantity>
        <price>10.90</price>
    </cd>
    <cd PurchaseDate="20000102">
        <title>Sylvias Mother</title>
        <artist>Dr.Hook</artist>
        <country>UK</country>
        <quantity>3</quantity>
        <price>8.10</price>
    </cd>
    <cd PurchaseDate="20000101">
        <title>Maggie May</title>
        <artist>Rod Stewart</artist>
        <country>UK</country>
        <quantity>8</quantity>
        <price>8.50</price>
    </cd>
    <cd PurchaseDate="20000103">
        <title>Romanza</title>
        <artist>Andrea Bocelli</artist>
        <country>EU</country>
        <quantity>30</quantity>
        <price>10.80</price>
    </cd>
</catalog>

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

<trans country="EU" PurchaseDate="20000103">
   <total>383.4</total>
</trans>
<trans country="UK" PurchaseDate="20000103">
   <total>174.4</total>
</trans>
<trans country="UK" PurchaseDate="20000102">
   <total>24.299999999999997</total>
</trans>
<trans country="UK" PurchaseDate="20000101">
   <total>218</total>
</trans>
<trans country="USA" PurchaseDate="20000102">
   <total>148.5</total>
</trans>
<trans country="USA" PurchaseDate="20000101">
   <total>218</total>
</trans>

Пояснение:

  1. Это нерекурсивное преобразование с двумя проходами. Рекурсивное решение XSLT 1.0 проблемы умножения чисел и последующего суммирования результатов умножения см. в ответе на этот вопрос: Умножьте 2 числа, а затем просуммируйте с помощью XSLT:

  2. Первый проход группируется по стране и дате покупки с использованием мюнхийского метода группировки с составным ключом.

  3. Для каждой группы создается несколько amount элементов.

  4. Второй проход поверхностно копирует transaction элементов, созданных в первом проходе. Он заменяет дочерние элементы amount одним элементом total.


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="/*">
  <xsl:for-each-group select="cd" group-by="concat(country,'+',@PurchaseDate)">
    <xsl:sort select="country"/>
    <xsl:sort select="@PurchaseDate" order="descending"/>

    <trans country="{country}" PurchaseDate="{@PurchaseDate}">
      <total><xsl:sequence select="sum(current-group()/(price*quantity))"/></total>
    </trans>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>
person Dimitre Novatchev    schedule 27.04.2013