Xpoint
   [напомнить пароль]

Группировка элементов с одноразовым выводом повторяющихся

Метки: [без меток]
2008-05-12 17:49:41 [обр] Andrey[досье]
сообщение промодерировано

Привет всем, у меня такой вопрос:
Есть xml:

<?xml version="1.0" encoding="UTF-8"?>
<goods>
   <item class="fruit">apple</item>
   <quantity>21</quantity>
   <cost>21</cost>
   
   <item class="fruit">pineapple</item>
   <quantity>11</quantity>
   <cost>101</cost>
   
   <item class="fruit">grape</item>
   <quantity>25</quantity>
   <cost>30</cost>
   
   <item class="fruit">grape</item>
   <quantity>40</quantity>
   <cost>20</cost>
   
   <item class="fruit">apple</item>
   <quantity>14</quantity>
   <cost>28</cost>
   
   <item class="fruit">pear</item>
   <quantity>17</quantity>
   <cost>20</cost>
   
   <item class="fruit">banana</item>
   <quantity>50</quantity>
   <cost>22</cost>
</goods>

нужно представить его в таком виде:

<fruits>
    <item id="apple">
        <quantity>35</quantity>
        <cost>49</cost>
    </item>
    <item id="pineapple">
        <quantity>11</quantity>
        <cost>101</cost>
    </item>
</fruits>

и т.д. То есть нужно вывести в новом виде, а те item которые повторяются просуммировать их значения quantity и cost и вывести эту сумму вместе с остальными в таком же виде.
появилась идея создать шаблон и изменить там структуру входящего xml и записать это в переменную так, чтобы можно было использовать ключи, сделал вот так:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
   <xsl:element name="All">
       <xsl:apply-templates select="goods/item"/>
   </xsl:element>
    </xsl:template>
    <xsl:template match="item">
   <xsl:variable name="parsed">
       <xsl:call-template name="structure"/>
   </xsl:variable>
        <xsl:for-each select="$parsed">
       <item>
           <xsl:attribute name="id"><xsl:value-of select="$parsed/item/@name"/></xsl:attribute>
      <quantity><xsl:value-of select="$parsed/item/@quantity"/></quantity>
      <cost><xsl:value-of select="$parsed/item/@cost"/></cost>
       </item>
   </xsl:for-each> 
    </xsl:template>
    <xsl:template name="structure">
   <item>
       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
       <xsl:attribute name="name"><xsl:value-of select="."/></xsl:attribute>
       <xsl:attribute name="quantity"><xsl:value-of select="following-sibling::quantity"/></xsl:attribute>
       <xsl:attribute name="cost"><xsl:value-of select="following-sibling::cost"/></xsl:attribute>
   </item>
    </xsl:template>
</xsl:stylesheet>

после этого в переменной $parsed содержится xml структура такого вида:

<All>
   <item class="fruit" name="apple" quantity="21" cost="21" />
   <item class="fruit" name="pineapple" quantity="11" cost="101" />
   <item class="fruit" name="grape" quantity="25" cost="30" />
   <item class="fruit" name="grape" quantity="40" cost="20" />
   <item class="fruit" name="apple" quantity="14" cost="28" />
   <item class="fruit" name="pear" quantity="17" cost="20" />
   <item class="fruit" name="banana" quantity="50" cost="22" />
</All>

и на выходе я получаю результат в нужном виде, но без суммирования повторяющихся:

<All>
   <item id="apple">
      <quantity>21</quantity>
      <cost>21</cost>
   </item>
   <item id="pineapple">
      <quantity>11</quantity>
      <cost>101</cost>
   </item>
   <item id="grape">
      <quantity>25</quantity>
      <cost>30</cost>
   </item>
   <item id="grape">
      <quantity>40</quantity>
      <cost>20</cost>
   </item>
   <item id="apple">
      <quantity>14</quantity>
      <cost>28</cost>
   </item>
   <item id="pear">
      <quantity>17</quantity>
      <cost>20</cost>
   </item>
   <item id="banana">
      <quantity>50</quantity>
      <cost>22</cost>
   </item>
</All>

помогите пожалуйста с суммированием, хотел через xsl:key делать, но ведь match в нем не может содержать переменных, а у меня структура, которую можно использовать с xsl:key содержится в переменной. или может есть какие то другие пути решения? извиняюсь что так много кода. спасибо заранее.

спустя 5 минут [обр] Lynn «Кофеман»(52/571)[досье]
спустя 8 минут [обр] Andrey[досье]
у меня проблема в том что я немогу добраться через xsl:key в переменную $parsed, а в ней и лежит структура на подобие той, на которую вы дали ссылку
спустя 34 минуты [обр] Lynn «Кофеман»(52/571)[досье]
А зачем что-то записывать в переменную?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

        <xsl:output method="xml" indent="yes" />
        <xsl:key name="group" match="item" use="." />

        <xsl:template match="/">
                <All>
                        <xsl:apply-templates select="goods/item[generate-id(.) = generate-id(key('group', .))]" />
                </All>
        </xsl:template>

        <xsl:template match="item">
                <item>
                        <xsl:attribute name="id">
                                <xsl:value-of select="." />
                        </xsl:attribute>
                        <quantity>
                                <xsl:value-of select="sum(key('group', .)/following-sibling::quantity[1])" />
                        </quantity>
                        <cost>
                                <xsl:value-of select="sum(key('group', .)/following-sibling::cost[1])" />
                        </cost>
                </item>
        </xsl:template>

</xsl:stylesheet>
спустя 15 минут [обр] Andrey[досье]
спасибо огромное! :) то что нужно. ура :)
спустя 1 час 55 минут [обр] Andrey[досье]
сообщение промодерировано

еще вопрос, не получается что-то никак :(
все таже задача, вот полный исходный xml

<goods>
   <item class="fruit">apple</item>
   <quantity>21</quantity>
   <cost>21</cost>
   
   <item class="fruit">pineapple</item>
   <quantity>11</quantity>
   <cost>101</cost>
   
   <item class="fruit">grape</item>
   <quantity>25</quantity>
   <cost>30</cost>
   
   <item class="fruit">grape</item>
   <quantity>40</quantity>
   <cost>20</cost>
   
   <item class="fruit">apple</item>
   <quantity>14</quantity>
   <cost>28</cost>
   
   <item class="fruit">pear</item>
   <quantity>17</quantity>
   <cost>20</cost>
   
   <item class="fruit">banana</item>
   <quantity>50</quantity>
   <cost>22</cost>
   
   <item class="vegetable">carrot</item>
   <quantity>50</quantity>
   <cost>10</cost>
   
   <item class="vegetable">potato</item>
   <quantity>60</quantity>
   <cost>8</cost>
   
   <item class="vegetable">red beet</item>
   <quantity>20</quantity>
   <cost>7</cost>
   
   <item class="vegetable">radish</item>
   <quantity>15</quantity>
   <cost>10</cost>
   
   <item class="vegetable">tomato</item>
   <quantity>30</quantity>
   <cost>25</cost>
   
   <item class="vegetable">cucumber</item>
   <quantity>35</quantity>
   <cost>30</cost>
   
   <item class="equipment">plastic bag</item>
   <quantity>1000</quantity>
   <cost>1</cost>
   
   <item class="equipment">tray</item>
   <quantity>100</quantity>
   <cost>5</cost>
   
   <item class="equipment">pot</item>
   <quantity>150</quantity>
   <cost>10</cost>
   
   <item class="equipment">basket</item>
   <quantity>100</quantity>
   <cost>15</cost>
</goods>

а нужно сделать

<All>
<fruits> 
   <item id="apple">
      <quantity>21</quantity>
      <cost>21</cost>
   </item>
   <item id="pineapple">
      <quantity>11</quantity>
      <cost>101</cost>
   </item>
   <item id="grape">
      <quantity>25</quantity>
      <cost>30</cost>
   </item>
   <item id="grape">
      <quantity>40</quantity>
      <cost>20</cost>
   </item>
   <item id="apple">
      <quantity>14</quantity>
      <cost>28</cost>
   </item>
   <item id="pear">
      <quantity>17</quantity>
      <cost>20</cost>
   </item>
   <item id="banana">
      <quantity>50</quantity>
      <cost>22</cost>
   </item>
</fruits>
<vegetable>
   <item id="carrot">
      <quantity>50</quantity>
      <cost>10</cost>
   </item>
   <item id="potato">
      <quantity>60</quantity>
      <cost>8</cost>
   </item>
   <item id="red beet">
      <quantity>20</quantity>
      <cost>7</cost>
   </item>
   <item id="radish">
      <quantity>15</quantity>
      <cost>10</cost>
   </item>
   <item id="tomato">
      <quantity>30</quantity>
      <cost>25</cost>
   </item>
   <item id="cucumber">
      <quantity>35</quantity>
      <cost>30</cost>
   </item>
</vegetables>
<equipment>
   <item id="plastic bag">
      <quantity>1000</quantity>
         <cost>1</cost>
   </item>
   <item id="tray">
      <quantity>100</quantity>
      <cost>5</cost>
   </item>
   <item id="pot">
      <quantity>150</quantity>
      <cost>10</cost>
   </item>
   <item id="basket">
      <quantity>100</quantity>
      <cost>15</cost>
   </item>
</equipment>
</All>

что нужно в коде дописать? дописываю тэмплейту в match="item[@class='fruit']" но он тогда значения остальных узлов выводит просто текстом

спустя 13 часов [обр] Lynn «Кофеман»(52/571)[досье]
Вообще-то надо думать головой.
Фактически у вас две группировки: по атрибуту class и по значению узла. Поэтому логично, что надо сделать два ключа и сгруппировать сначала по первому, а потом по второму.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

        <xsl:output method="xml" indent="yes" />
        <xsl:key name="class" match="item" use="@class" />
        <xsl:key name="group" match="item" use="." />

        <xsl:template match="/">
                <All>
                        <xsl:apply-templates
                                select="goods/item[generate-id(.) = generate-id(key('class', @class))]"
                                mode="class"
                        />
                </All>
        </xsl:template>

        <xsl:template match="item" mode="class">
                <xsl:element name="{@class}">
                        <xsl:apply-templates
                                select="../item[generate-id(.) = generate-id(key('group', .)[@class = current()/@class])]"
                        />
                </xsl:element>
        </xsl:template>

        <xsl:template match="item">
                <item>
                        <xsl:attribute name="id">
                                <xsl:value-of select="." />
                        </xsl:attribute>
                        <quantity>
                                <xsl:value-of select="sum(key('group', .)[@class = current()/@class]/following-sibling::quantity[1])" />
                        </quantity>
                        <cost>
                                <xsl:value-of select="sum(key('group', .)[@class = current()/@class]/following-sibling::cost[1])" />
                        </cost>
                </item>
        </xsl:template>

</xsl:stylesheet>
спустя 2 часа 51 минуту [обр] Andrey[досье]
извиняюсь, только недавно начал изучать xsl еще незнаю всех тонкостей, спасибо большое
Powered by POEM™ Engine Copyright © 2002-2005