Форум: "Прочее";
Текущий архив: 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