Гость
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / XSLT: Двухуровневая группировка / 17 сообщений из 17, страница 1 из 1
29.05.2013, 10:07
    #38277888
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Помогите составить XSLT.

Есть такие исходные данные:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<Data>
  <References>
    <Categories>
      <Category name="cat1" title="Категория 1" index="1" />
      <Category name="cat2" title="Категория 2" index="2" />
      <Category name="cat3" title="Категория 3" index="3" />
    </Categories>
    <Groups>
      <Group name="grp1" title="Группа 1" index="3" description="notes 1"/>
      <Group name="grp2" title="Группа 2" index="2" description="notes 2"/>
      <Group name="grp3" title="Группа 3" index="1" description="notes 3"/>
    </Groups>
  </References>
  <Items>
    <Item index="1" name="item1" category="cat1" group="grp1" ... />
    <Item index="2" name="item2" category="cat2" group="grp1" ... />
    <Item index="3" name="item3" category="cat3" group="grp2" ... />
    <Item index="4" name="item4" category="cat3" group="grp2" ... />
  </Items>
</Data>



Мне нужно вывести Items/Item, сгруппировав их вначале по группе, затем по категории. Группы сортируются по индексу группы, категории сортируются по индексу категории, внутри группы элементы сортируются по индексу элемента.
То есть мне нужно получить примерно такую структуру:
Код: html
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<h1>Группа 2</h1>
<p>notes 2</p>

<h2>Категория 3</h2>
<ol>
<li>item3</li>
<li>item4</li>
</ol>

<h1>Группа 1</h1>
<p>notes 1</p>

<h2>Категория 1</h2>
<ol>
<li>item1</li>
</ol>

<h2>Категория 2</h2>
<ol>
<li>item2</li>
</ol>



Как это сделать? В for-each-group можно задавать только один ключ для группировки.

И еще я бы хотел сделать XSLT не в одном общем шаблоне, а в трех шаблона (для заголовка группы, для заголовка категории и для списка элементов), но не соображу, как для шаблона задавать атрибут match.

________________________
Мы смотрим с оптимизмом...
...в оптический прицел.
...
Рейтинг: 0 / 0
29.05.2013, 10:16
    #38277903
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Вот тут я решал похожую проблему: 13107213
...
Рейтинг: 0 / 0
29.05.2013, 10:19
    #38277907
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
На stackoverflow я нашел несколько примеров с группировкой Мюнха.
Но все примеры исходят из того, что все используемые атрибуты заданы непосредственно для элемента.
У меня же сортировку нужно осуществлять по атрибуту из другого узла.
Можно в крайнем случае сделать два преобразования — первый раз в Item добавить атрибуты group_index и category_index, а вторым проходом группировать по ним — но это как-то неправильно.
...
Рейтинг: 0 / 0
29.05.2013, 10:22
    #38277914
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Посмотрел внимательнее — абсолютно тоже самое, что у меня. Только названия групп и категорий лежат отдельно.
...
Рейтинг: 0 / 0
29.05.2013, 10:23
    #38277919
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
AntonariyТолько названия групп и категорий лежат отдельно.
В этом и сложность.
...
Рейтинг: 0 / 0
29.05.2013, 10:58
    #38278036
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Еще вопрос. В исходных данных я немного неправильно структуру привел.
Правильнее узлы Group выглядят так:
Код: xml
1.
<Group name="grp1" title="Группа 1" index="3">notes 1</Group>



Как мне в XSL получить содержимое узла? Так можно?
Код: xml
1.
<xsl:if test="key('group',@group)/text()">key('group',@group)/text()</xsl:if>
...
Рейтинг: 0 / 0
29.05.2013, 11:01
    #38278050
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Alibek B.AntonariyТолько названия групп и категорий лежат отдельно.
В этом и сложность.Выборка из известного раздела по известным условиям? В чем тут сложность?

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<xsl:key name="item" match="row" use="@group"/>

  <xsl:template match="/Data/Items/*">
    <root>
      <xsl:apply-templates select="*[generate-id(.)=generate-id(key('item',@group))]" mode="key"/>
    </root>
  </xsl:template>

  <xsl:template match="Item" mode="key">
    <h1><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@title"/></h1>
    <p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@description"/></p>      

    <xsl:apply-templates select="parent::*/*[not(@category=preceding-sibling::*/@category)]" mode="category">
        <xsl:sort select="@category" data-type="text" order="ascending" />
        <xsl:with-param name="group" select="@group"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="Item" mode="category">
    <xsl:param name="group"/>
    <h2><xsl:value-of select ="/Data/References/Categories/*[@name=current()/@category]/@title"/></h2>
    <ol><xsl:apply-templates select="parent::*/*[@group=$group][@category=current()/@category]" mode="category"/></ol>
  </xsl:template>

  <xsl:template match="row" mode="category">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>
...
Рейтинг: 0 / 0
29.05.2013, 11:04
    #38278058
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Alibek B.Еще вопрос. В исходных данных я немного неправильно структуру привел.
Правильнее узлы Group выглядят так:
Код: xml
1.
<Group name="grp1" title="Группа 1" index="3">notes 1</Group>


Код: xml
1.
<p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/text()"/></p>
...
Рейтинг: 0 / 0
29.05.2013, 11:06
    #38278065
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Ошибочка.
Код: xml
1.
<xsl:key name="item" match="Item" use="@group"/>
...
Рейтинг: 0 / 0
29.05.2013, 11:09
    #38278077
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Еще ошибки нашел. Вот окончательный вариант:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<xsl:key name="item" match="Item" use="@group"/>

  <xsl:template match="/Data/Items/*">
    <root>
      <xsl:apply-templates select="*[generate-id(.)=generate-id(key('item',@group))]" mode="key"/>
    </root>
  </xsl:template>

  <xsl:template match="Item" mode="key">
    <h1><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@title"/></h1>
    <p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/text()"/></p>

    <xsl:apply-templates select="parent::*/*[not(@category=preceding-sibling::*/@category)]" mode="categorylist">
        <xsl:sort select="@category" data-type="text" order="ascending" />
        <xsl:with-param name="group" select="@group"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="Item" mode="categorylist">
    <xsl:param name="group"/>
    <h2><xsl:value-of select ="/Data/References/Categories/*[@name=current()/@category]/@title"/></h2>
    <ol><xsl:apply-templates select="parent::*/*[@group=$group][@category=current()/@category]" mode="category"/></ol>
  </xsl:template>

  <xsl:template match="Item" mode="category">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>
...
Рейтинг: 0 / 0
29.05.2013, 11:20
    #38278100
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Сделал вот так:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
...
<xsl:key name="group"  match="/Data/References/Groups/Group" use="@name"/>
<xsl:key name="category" match="/Data/References/Categories/Category" use="@name"/>
<xsl:key name="item_group"  match="/Data/Items/Item" use="@group"/>
<xsl:key name="item_category"  match="/Data/Items/Item" use="concat(@group,'|',@category)"/>
...

<table>

<xsl:for-each select="//Items/Item[generate-id() = generate-id(key('item_group',@group)[1])]">
<xsl:sort select="key('group',@group)/@index"/>
<!-- group GROUP -->
<tr>
<td colspan="3">
<h2"><xsl:value-of select="key('group',@group)/@name"/></h2>
<xsl:if test="key('group',@group)/text()"><xsl:value-of select="key('group',@group)/text()"/></xsl:if>
</td>
</tr>
<!-- /group GROUP -->

<xsl:for-each select="key('item_group',@group)[generate-id() = generate-id(key('item_category',concat(@group,'|',@category))[1])]">
<xsl:sort select="key('category',@category)/@index"/>
<!-- CATEGORY GROUP -->
<tr>
<td colspan="3"><h3"><xsl:value-of select="key('category',@category)/@title"/></h3></td>
</tr>
<!-- /CATEGORY GROUP -->

<xsl:for-each select="key('item_category',concat(@group,'|',@category))">
<xsl:sort select="@index"/>
<!-- ITEM LIST -->
<tr>
<td><img src="..."/></td>
<td><span><xsl:value-of select="@title"/></span><span>#<xsl:value-of select="@index"/></span><br/>
<xsl:if test="text()"><xsl:value-of select="text()"/></xsl:if><xsl:if test="not(text())"><span style="color:gray;font-style:oblique;">без описания</span></xsl:if></td>
<td>etc.</td>
</tr>
<!-- /ITEM LIST-->
</xsl:for-each>

</xsl:for-each>

</xsl:for-each>

</table>



В целом структура примерно такая, какая мне нужна.
Однако внутри группы элементы почему-то не сортируются.
Что нужно исправить?
...
Рейтинг: 0 / 0
29.05.2013, 11:23
    #38278107
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Antonariy<xsl:param name="group"/>
Действительно, про параметры я забыл.
...
Рейтинг: 0 / 0
29.05.2013, 11:27
    #38278116
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Alibek B.Что нужно исправить?
Разобрался, нужно было числовой тип данных указать (data-type="number").
Он у меня сортировал @index как текст.
...
Рейтинг: 0 / 0
29.05.2013, 11:38
    #38278145
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Еще вопрос, можно ли пронумеровать все элементы (сквозная нумерация)?
position() не подходит, он начинает нумерацию с начала в каждой группе.
...
Рейтинг: 0 / 0
30.05.2013, 13:28
    #38280113
mage.lan
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Alibek B.,

зачем для такой простой задачи, такое сложное решение? Как описали, так и надо решать.
Что касается нумерации, если нет избыточных данных, то можно сделать, если есть... будет нетривиально, с помощью рекурсии, либо с помощью двойной трансформации, итемы набираются в переменную, там где надо вставить группы/категории у итемов прописывается что-то, потом все гонится в один for на выход с проверкой, не надо ли порубить OL с помощью CDATA.

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
	<xsl:output method="html" indent="yes" />

	<xsl:template match="/">
		<xsl:variable name="items" select="/Data/Items/Item" />
		<xsl:for-each select="/Data/References/Groups/Group">
			<xsl:sort select="@index" data-type="number"/>
			<xsl:variable name="group" select="." />
			<xsl:for-each select="/Data/References/Categories/Category">
				<xsl:sort select="@index" data-type="number"/>
				<xsl:variable name="category" select="." />
				<xsl:variable name="set" select="$items[@category=$category/@name and @group=$group/@name]" />
				<xsl:if test="count($set)>0">
					<H1>
						<xsl:value-of select="$group/@title" />
					</H1>
					<H2>
						<xsl:value-of select="$category/@title" />
					</H2>
					<OL>
						<xsl:for-each select="$set">
							<LI>
								<xsl:value-of select="@name" />
							</LI>
						</xsl:for-each>
					</OL>
				</xsl:if>
			</xsl:for-each>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>
...
Рейтинг: 0 / 0
30.05.2013, 14:37
    #38280246
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
mage.lan , про такой способ я тоже думал.
Но у него есть два недостатка. А в предложенной реализации даже три.
1. В предложенной реализации заголовок группы выводится для каждой категории. Мне заголовок группы нужно выводить только один раз, то есть этот фрагмент нужно переместить во внешний цикл.
2. Могут выводится пустые заголовки. В предложенной реализации считается число узлов, соответствующих критерию, чтобы не выводить пустой заголовок. Но с учетом п.1 такую проверку нужно делать дважды.
3. Число записей у меня ожидается достаточно большое (минимум несколько сотен). При таких значениях key будет гораздо быстрее, чем вычисление $items[@category=$category/@name and @group=$group/@name] на каждом проходе. И кстати, разве в XSLT1 можно использовать переменную для ссылки на узел? Помоему там были какие-то сложности.
...
Рейтинг: 0 / 0
30.05.2013, 17:32
    #38280609
mage.lan
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
XSLT: Двухуровневая группировка
Alibek B.,

1) не проблема, в первом цикле заводим переменную pos = position()
вокруг h1 делаем проверку на $pos = 1, нет проблемы.

2) не нужно делать проверку дважды, вторая проверка будет делаться внутри

3) приведенный код жрет любой парсер, даже браузерный, проблемы с переменными в другом.
И еще, ваша реализация с ключами и то, как вы их используете, медленнее. Парсеры оптимизированы на простые выборки. Не верите - проверяйте. На 1000 записей вы вообще разницы не увидите в скорости.

Ну и еще, когда вы через 2 месяца полезете править свой код, волосы на голове у вас начнут шевелится, т.к. вы вообще не поймете, что там было написано, в поддержке мой вариант как бы тоже предпочтительнее.
...
Рейтинг: 0 / 0
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / XSLT: Двухуровневая группировка / 17 сообщений из 17, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]