Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.08.02;
Скачать: CL | DM;

Вниз

Группировка в XSL   Найти похожие ветки 

 
Юрий Зотов ©   (2009-05-25 10:05) [0]

Eсть такой XML:

<Документ>
 <Дата>01.01.2007</Дата>
 <Число>100.00</Число>
</Документ>
<Документ>
 <Дата>02.02.2007</Дата>
 <Число>200.00</Число>
</Документ>
<Документ>
 <Дата>01.01.2008</Дата>
 <Число>300.00</Число>
</Документ>
<Документ>
 <Дата>02.02.2008</Дата>
 <Число>400.00</Число>
</Документ>
<Документ>
 <Дата>01.01.2009</Дата>
 <Число>500.00</Число>
</Документ>
<Документ>
 <Дата>02.02.2009</Дата>
 <Число>600.00</Число>
</Документ>
и т.д.

Документы не отсортированы. Формат даты: dd.MM.yyyy. Количество документов произвольное, но документов за один год вряд ли более 12, а за все годы - вряд ли более 500.

Задача: написать XSL-код, который выводит документы в возрастающем по дате порядке, при этом группируя их по годам и вставляя суммы чисел за год.

То есть:

Дата            Число
--------------------
01.01.2007    100.00
02.02.2007    200.00
За 2007 год   300.00
01.01.2008    300.00
02.02.2008    400.00
За 2008 год   700.00
01.01.2009    500.00
02.02.2009    600.00
За 2009 год  1100.00

Есть проблема, с которой не могу справиться (ну чайник я XSL, ну что поделаешь, все мы там были). Проблема эта состоит в том, что в цикле for-each (перечисляющим документы) нужно:
а). поймать момент "перещелкивания" года;
б). в этот момент рассчитать сумму чисел за год.

По группировке материалы в сети есть, читал их - но тут особенность: группировать надо не по всему полю "Дата", а только ее части (по году). Да и как считать сумму по выборке за год - это пока тоже неясно (есть мысль использовать рекурсивный call-template с параметром, но из-за рекурсии эта мысль мне даже и самому не нравится).

Обращаюсь к знатокам XSLT с просьбой помочь. Заранее спасибо.


 
makvell   (2009-05-25 11:33) [1]

Ну... я могу показать как схимичить такое чудо :) Примерно, конечно нужно будет чутка допилить... куда отправить/выложить?


 
makvell   (2009-05-25 11:47) [2]

Примерно вот так.

[url]http://www.sourcepod.com/macmpn25-436[/url]

Порядок там такой:
документ,
трансформация,
результат.

Версия 2.0, так как в 1.0 в переменную нельзя так засунуть ноде-сет, получается только резалт три :(

Я бы, на самом деле, сделал два трансформа, один сортирует и форматирует в промежуточный вид, второй просто идет по результату первого и уже выдает окончательный вид, тогда можно использовать 1.0 :)

На идеальность не претендую, но решение рабочее :)


 
makvell   (2009-05-25 11:48) [3]

Блин, ссылка как-то коряво вставилась... ну да ладно, кому нужно, тот поймет :)


 
Юрий Зотов ©   (2009-05-25 12:16) [4]

> makvell   (25.05.09 11:33) [1]

> Ну... я могу показать как схимичить такое чудо :)
Спасибо. И за ссылку тоже (сейчас пойду смотреть). Хотя примеры с result tree я уже видел, но там не совсем то.

>Примерно, конечно нужно будет чутка допилить...
Дык... обычное дело.

> куда отправить/выложить?
Дык... а почему бы не прямо сюда?

> Я бы, на самом деле, сделал два трансформа, один сортирует и
> форматирует в промежуточный вид, второй просто идет по результату
> первого и уже выдает окончательный вид, тогда можно использовать 1.0 :)

Я бы, на самом деле, может, так бы и сделал... если бы не был чайником и знал, как это делается....
:о)

PS
Совсем недавно начал ковыряться в этом убогом языке... что ж странного-то?


 
makvell   (2009-05-25 13:24) [5]

Ну дык :)

Тогда можно так и сделать :)

1 трансформ сортирует и приводит в удобный для второго вид, а 2 трансформ выдает уже то что нужно...

Все что потребуется, по ссылке есть, только разделить на два трансформа :)

Кстати, нифига оно не убогое :) очень мощная штука, а изучать лучше по спеке, больше ничего не нужно, там все описано... она точно есть в сети переведенная, хотя и на английском все понятно :)


 
Медвежонок Пятачок ©   (2009-05-25 15:32) [6]

http://pinstaller.narod.ru/sample.xml


 
makvell   (2009-05-25 16:02) [7]

Ну да, тоже вариант... только тоже с извратами :)

ЗЫ <xsl:out еще добавить, а то читать коряво ;)


 
Медвежонок Пятачок ©   (2009-05-25 16:23) [8]

Там исходная структура xml продумана плохо. точнее совсем не продумана.
Отсюда и извраты.


 
Юрий Зотов ©   (2009-05-25 23:04) [9]

> makvell
Огромное спасибо.

> Медвежонок Пятачок
1. Тоже спасибо.
2. Структура XML дана свыше и утверждена Богами. Обсуждать ее можно, но бессмыссленно.

> All
Хотел сделать попроще и получилось вот что:

<?xml version="1.0" encoding="Windows-1251" ?>

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

<xsl:output method="text" indent="yes" encoding="Windows-1251"/>

<xsl:template match="/">
 
 <xsl:for-each select="Документ">
 
   <xsl:sort select="concat(substring(Дата,7,4),substring(Дата,4,2),substring(Дата,1,2))"/>
 
   <xsl:value-of select="concat(Дата," ")"/>
   <xsl:value-of select="Число"/>
 
   <xsl:variable name="curyear">
     <xsl:value-of select="substring(Дата,7,4)"/>
   </xsl:variable>
 
   <xsl:variable name="nextyear">
     <xsl:value-of select="substring(following-sibling::document/Дата,7,4)"/>
   </xsl:variable>
 
   <xsl:if test="(position() = last()) or (not($curyear = $nextyear))">
     <xsl:call-template name="СуммаЗаГод">
       <xsl:with-param name="year" select="$curyear"/>
     </xsl:call-template>
   </xsl:if>

 </xsl:for-each>

</xsl:template>

<!--================================================ -->

<xsl:template name="СуммаЗаГод">
 <xsl:param name="year"/>

 <xsl:value-of select="concat("За ",$year," год ")"/>
 <xsl:value-of select="sum(../Документ[substring(@Дата,7,4) = $year]/@Число)"/>

</xsl:template>

</xsl:stylesheet>

Результат: строчки "СуммаЗаГод" вставляются на нужные места и год в них правильный. А вот сумма - всегда нулевая.

Читаю доку - там написано: number sum(node-set)
Вроде, у меня так и есть. Но сумма не вычисляется.

Сравниваю с примером (еще раз спасибо) - вроде бы, отличия несущественные. Но сумма не вычисляется.

В общем, либо сам дурак, либо процессор XSLT что-то не поддерживает (пишу на java, использую XALAN 2.7.1).

Братцы, подскажите - где я лопухнулся?


 
Юрий Зотов ©   (2009-05-25 23:56) [10]

поправка: following-sibling::document[1]


 
Медвежонок Пятачок ©   (2009-05-26 00:41) [11]

<xsl:value-of select="sum(../Документ[substring(@Дата,7,4) = $year]/@Число)"/>

@Дата - это обращение к атрибуту. Но его же нет.
дата там в узле Дата

С "@Число" - то же самое


 
Юрий Зотов ©   (2009-05-26 09:31) [12]

> Медвежонок Пятачок

Убрал собачки:
<xsl:value-of select="sum(../Документ[substring(Дата,7,4) = $year]/Число)"/>
и заработало. В том числе, и при stylesheet version="1.0".

Спасибо вам, люди.

Последняя просьба - покритиковать алгоритм и код.  Надеюсь, делать это будет приятно.
:o)


 
Медвежонок Пятачок ©   (2009-05-26 09:48) [13]

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

улучшайзер скорее всего тоже возможен, например через for-each-group-by
но у меня эта конструкция не заводится


 
makvell   (2009-05-26 10:40) [14]

Ну вот :)

Все получилось, с чем и проздравляю :)

На счет улучшений... в версии 2.0 там какие-то специальные фигулины есть для группировок (кажется), но их я еще не смотрел, возможно используя их можно как-то улучшить...

Кстати, а как насчет скорости на больших документах? Если она вменяемая, то можно и так оставить, все же работает и весьма красиво написано :) , а вот если она не очень, возможно можно покурить в сторону <xsl:key... .


 
Юрий Зотов ©   (2009-05-26 12:06) [15]

> makvell   (26.05.09 10:40) [14]

> как насчет скорости на больших документах?

Пока не знаю. Жизнь покажет. Спасибо за помощь и советы.

C key надо будет разобраться, я пока в нее как следует не въехал (почему и не стал копировать пример один-в-один). По сути, как я понял, эта конструкция похожа на джавский Map (хранилище пар key-value) и если это так, то штука удобная.


 
Юрий Зотов ©   (2009-05-26 12:27) [16]

> makvell
> Медвежонок Пятачок

Кстати, можете слегка погордиться. Не без вашего участия, эта штука в ближайшее же время начнет работать на 600 серверах по всей стране и считать живые деньги десятков миллионов живых людей (практически - все трудоспособное население России).

Конечно, реальные XML и XSL намного сложнее, чем здесь было показано, но суть та же.

Так что - имеем право скромно посмотреть в зеркало...
:o)


 
makvell   (2009-05-26 12:37) [17]

:)


 
Питер фром москоу   (2009-05-27 11:09) [18]

Товарищи, посмотрел я тут, посмотрел.. .Мне кажется пришел к выводу, что XSL это что-то нереально убого...

Есть один вопрос - а нафига, в чем плюсы?


 
Медвежонок Пятачок ©   (2009-05-27 12:43) [19]

в том, что имея данные в одном xml, можно с помощью разных xsl превратить их в:
- "более другой" xml
- другой xml который превратится в датапакет для клиентдатасета или адодатасета
- html
- plain text
- rich text
- во многое другое.


 
SPeller ©   (2009-05-28 08:23) [20]


> Есть один вопрос - а нафига, в чем плюсы?

Достаточно почитать для чего вообще XSL был придуман.


 
Юрий Зотов ©   (2009-05-28 15:27) [21]

Блин. Снова я за подсказкой.

Рано я радовался и к зеркалу бегал. Более аккуратное тестирование показало, что не все так гладко. На этот раз суммы считаются правильно, но строка "за год" вставляется не там, где надо (раз) и может дублироваться (два).

Исходный XML:

<Документ>
 <Дата>31.12.2009</Дата>
 <Число>1.01</Число>
</Документ>
<Документ>
 <Дата>31.12.2008</Дата>
 <Число>2.02</Число>
</Документ>
<Документ>
 <Дата>01.01.2008</Дата>
 <Число>4.04</Число>
</Документ>
 

Вывод:

01.01.2008    4.04
За 2008 год:  6.06 Эта строка должна быть на 1 ниже.
31.12.2008    2.02 Или эта на 1 выше
31.12.2009    1.01
За 2009 год:  1.01


Еще пример. Исходный XML:

<Документ>
 <Дата>31.12.2009</Дата>
 <Число>10.04</Число>
</Документ>
<Документ>
 <Дата>01.01.2009</Дата>
 <Число>20.05</Число>
</Документ>
<Документ>
 <Дата>01.01.2008</Дата>
 <Число>30.06</Число>
</Документ>
 

Вывод:

01.01.2008    30.06
За 2008 год:  30.06
01.01.2009    20.05
За 2009 год:  30.09  Этой строки быть не должно
31.12.2009    10.04
За 2009 год:  30.09

Резюме:
1. При перещелкивании года строка "ЗаГод" появляется на 1 позицию раньше, чем нужно.
2. Если при этом position() = last(), то появляются 2 строки вместо одной.

Крутил условие в IF по-всякому - ничего хорошего так и не добился. А самое плохое то, что ошибок в нем я не вижу. ИМХО, должно работать - но не работает. В чем дело - не понимаю.


 
makvell   (2009-05-28 16:24) [22]

И снова хао :)
Глубоко не копался, но есть предположение, что он ошибается тут:
<xsl:variable name="nextyear">
   <xsl:value-of select="substring(following-sibling::Документ[1]/Дата,7,4)"/>
</xsl:variable>

тут он смотри на следующих узел, но в текущем дереве, а не в отсортированном :)

Все же советую сделать так:


 
makvell   (2009-05-28 16:25) [23]

Это исходный документ:

<?xml version="1.0" encoding="UTF-8"?>
<docs>
<doc>
 <date>31.12.2009</date>
 <num>1.01</num>
</doc>
<doc>
 <date>31.12.2008</date>
 <num>2.02</num>
</doc>
<doc>
 <date>01.01.2008</date>
 <num>4.04</num>
</doc>
</docs>


 
makvell   (2009-05-28 16:26) [24]

Делаем из него то что нам нужно, т.е. документ отсортированный:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" media-type="text/xml" encoding="UTF-8"/>
<xsl:template match="/">
 <docs>
  <xsl:apply-templates select="docs"/>
 </docs>
</xsl:template>
<xsl:template match="docs">
 <xsl:for-each select="doc">
  <xsl:sort select="concat(substring(date,7,4),substring(date,4,2),substring(date,1,2))"/>
  <doc date="{date/text()}" num="{num/text()}"/>
 </xsl:for-each>
</xsl:template>
</xsl:stylesheet>


 
makvell   (2009-05-28 16:26) [25]

Получаем примерно это:

<?xml version="1.0" encoding="UTF-8"?>
<docs xmlns:fo="http://www.w3.org/1999/XSL/Format">
<doc date="01.01.2008" num="4.04"/>
<doc date="31.12.2008" num="2.02"/>
<doc date="31.12.2009" num="1.01"/>
</docs>


 
makvell   (2009-05-28 16:27) [26]

Ну и делаем уже то что нам нужно:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" media-type="text/xml" encoding="UTF-8"/>
<xsl:template match="/">
 <result>
  <xsl:for-each select="docs/doc">
   <xsl:variable name="curyear">
    <xsl:value-of select="substring(@date,7,4)"/>
   </xsl:variable>
   <xsl:variable name="prevyear">
    <xsl:value-of select="substring(preceding-sibling::doc[1]/@date,7,4)"/>
   </xsl:variable>
   <xsl:if test="position() != 1 and $curyear != $prevyear">
    <summary num="{sum(../doc[substring(@date,7,4) = $prevyear]/@num)}"/>
   </xsl:if>
   <document date="{@date}" num="{@num}"/>
   <xsl:if test="position() = last()">
    <summary num="{sum(../doc[substring(@date,7,4) = $curyear]/@num)}"/>
   </xsl:if>
  </xsl:for-each>
 </result>
</xsl:template>
</xsl:stylesheet>

Тут я менять не стал, можно завязаться как у вас, т.е. не на предыдущий, а на следующий, чтобы не проверять два раза. Ну и тут все с выводом в XML, но думаю что проблем с переделкой на то что нужно не будет :)



Страницы: 1 вся ветка

Текущий архив: 2009.08.02;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.012 c
3-1225096677
dolmat
2008-10-27 11:37
2009.08.02
Количество месяцев


3-1225086355
hic
2008-10-27 08:45
2009.08.02
Передача данных из сохраненного отчета FastReport в Delphi


2-1242554718
jonin
2009-05-17 14:05
2009.08.02
Domain_name_IP_Traffic


2-1244443734
saNat
2009-06-08 10:48
2009.08.02
Создание БД Access на основе существующего "каркаса"


15-1243629004
Юрий
2009-05-30 00:30
2009.08.02
С днем рождения ! 30 мая 2009 суббота