Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.08.02;
Скачать: [xml.tar.bz2];

Вниз

Группировка в 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.023 c
15-1243512133
oldman
2009-05-28 16:02
2009.08.02
Сбылась мечта идиота...


2-1244224996
Nekroraise
2009-06-05 22:03
2009.08.02
цвет TColor в формат цвета фотошопа(и не только)..


15-1244017108
i2e
2009-06-03 12:18
2009.08.02
Help и Vista


15-1243456205
Юрий
2009-05-28 00:30
2009.08.02
С днем рождения ! 28 мая 2009 четверг


2-1244466461
Arcticcat
2009-06-08 17:07
2009.08.02
Помогите с GDI+





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский