Гость
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / Оглавление / 10 сообщений из 10, страница 1 из 1
20.04.2010, 19:56
    #36588678
cavalero
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Добрый день!
Потратил целый день и чуть не лишился твёрдого рассудка. Есть вот такой XML,
Код: plaintext
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.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
<hotels>
  <hotel title="CARLSBAD PLAZA">
    <hotelinfo>
      <description category="*****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Карловы Вары" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="ESPLANADE">
    <hotelinfo>
      <description category="*****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Марианские Лазни" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="CISARSKE LAZNE">
    <hotelinfo>
      <description category="****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Теплице" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="OLYMPIA">
    <hotelinfo>
      <description category="****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Марианские Лазни" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="CENTRALNI LAZNE">
    <hotelinfo>
      <description category="****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Марианские Лазни" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="Four Seasons">
    <hotelinfo>
      <description category="***" hoteltype="Hotel" />
      <country title="France">
        <city title="Париж" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="LIVIA">
    <hotelinfo>
      <description category="*****" hoteltype="SPA" />
      <country title="Czech Republic">
        <city title="Карловы Вары" />
      </country>
    </hotelinfo>
  </hotel>
  <hotel title="BRISTOL">
    <hotelinfo>
      <description category="****" hoteltype="Hotel" />
      <country title="Czech Republic">
        <city title="Карловы Вары" />
      </country>
    </hotelinfo>
  </hotel>
</hotels>
Как можно из него получить такую структуру?
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<countries>
<country title="Czech Republic">
<cities>
<city title="Карловы Вары">
<hoteltypes>
<hoteltype title="Hotel">
<hotels category="***" title="BRISTOL">
<hotel >
</hotel>
</hotels>
</category>
</hoteltype>
</hoteltypes>
</city>
</cities>
</country>
</countries>

Т.е. нужно сделать distinct + group, а как -- не ясно совершенно.
...
Рейтинг: 0 / 0
20.04.2010, 20:00
    #36588685
cavalero
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Сорри, я, конечно не хочу, чтобы кто-то за меня это сделал. Просто нужен совет от людей разбирающихся в вопросе в каком направлении двигаться.

Спасибо.
...
Рейтинг: 0 / 0
22.04.2010, 15:47
    #36592658
cavalero
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Хоть никто и не помог, но! Делается это так:
Не оптимально? Да! Но работает!

Код: plaintext
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.
						<fo:block break-after="page" padding-top="8pt" padding-bottom="8pt">
							<fo:block font-size="16pt" text-align="center">Содержание</fo:block>


							<xsl:variable name="countries" select="//hotel/hotelinfo/country[not(@title=following::country/@title)]" />
							<xsl:for-each select="$countries">
								<xsl:sort select="@title" order="ascending" data-type="text"/>
								<xsl:variable name="country" select="@title" />
								<xsl:variable name="cities" select="//hotel/hotelinfo/country[@title=$country]/city[not(@title=following::city/@title)]" />
								<xsl:for-each select="$cities">
									<xsl:sort select="@title" order="ascending" data-type="text"/>
									<xsl:variable name="city" select="@title" />
									<xsl:variable name="htypes" select="//hotel/hotelinfo/description[not(@hoteltype=following::description/@hoteltype)]" />
									<xsl:for-each select="$htypes">
										<xsl:sort select="@hoteltype" order="ascending" data-type="text"/>
										<xsl:variable name="hoteltype" select="@hoteltype" />
										<xsl:if test="count(//hotel[hotelinfo/description/@hoteltype=$hoteltype and hotelinfo/country[@title=$country]/city[@title=$city]])>0">
											<fo:block text-align="center" display-align="center" border="0.5pt solid black" padding="2mm" background-color="lightgrey" font-weight="bold" margin-top="8pt" margin-bottom="8pt">
												<xsl:value-of disable-output-escaping="yes" select="$country" />, <xsl:value-of disable-output-escaping="yes" select="$city" />, <xsl:value-of disable-output-escaping="yes" select="$hoteltype" />
											</fo:block>
											<xsl:for-each select="//hotel[hotelinfo/description/@hoteltype=$hoteltype and hotelinfo/country[@title=$country]/city[@title=$city]]">
												<xsl:sort select="hotelinfo/description/@categoryid" order="descending" data-type="number"/>
												<xsl:sort select="hotelinfo/description/@title" order="ascending" data-type="text"/>
												<fo:block text-align-last="justify" font-size="10pt">
													<fo:basic-link internal-destination="chapter{concat($country, $city, $hoteltype, hotelinfo/description/@title,position())}" color="blue" >
														<xsl:value-of select="hotelinfo/description/@title" />
													</fo:basic-link>
													 <xsl:value-of select="hotelinfo/description/@category" />
													<fo:inline keep-together.within-line="always">
														<fo:leader leader-pattern="dots" />
														<fo:page-number-citation ref-id="chapter{concat($country, $city, $hoteltype, hotelinfo/description/@title,position())}" />
													</fo:inline>
												</fo:block>
											</xsl:for-each>
										</xsl:if>
									</xsl:for-each>
								</xsl:for-each>
							</xsl:for-each>



						</fo:block>
					</fo:block>
...
Рейтинг: 0 / 0
15.06.2010, 18:24
    #36688400
Andry Trushin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
cavalero,

ты почти правильно все сделал, за исключением перебора стран.

если тебе еще интересно "как правильно", найди на википедии статью про XSL, там есть мой пример с xsl:key.
...
Рейтинг: 0 / 0
17.06.2010, 09:39
    #36691589
walek
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Можете посмотреть как делал я
http://valiullin.livejournal.com/13651.html
...
Рейтинг: 0 / 0
22.06.2010, 23:24
    #36701905
y-niko
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Заморочился. Интересная задача. Получилось следующее преобразование:
Код: plaintext
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.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
	
	<xsl:key name="country_plus_city_hotels" match="hotel" use="concat(string(descendant::country/attribute::title),string(descendant::country/city/attribute::title))"/>
	<xsl:key name="countries" match="country" use="attribute::title"/>
	
	<xsl:template match="/">
		<xsl:element name="countries">
			<xsl:apply-templates select="descendant::country"/>
		</xsl:element>
	</xsl:template>

	<xsl:template match="country">
		<xsl:variable name="country_title" select="attribute::title"/>
		<xsl:if test="generate-id(.)=generate-id(key('countries',$country_title)[1])">
			<xsl:element name="country">
				<xsl:attribute name="title">
					<xsl:value-of select="$country_title"/>
				</xsl:attribute>
						<xsl:apply-templates select="/./descendant::country[attribute::title=$country_title]/city[not(attribute::title=preceding::country[attribute::title=$country_title]/city/attribute::title)]">
							<xsl:with-param name="country_title" select="$country_title"/>
						</xsl:apply-templates>
			</xsl:element>
		</xsl:if>
		
	</xsl:template>

	<xsl:template match="city">
		<xsl:param name="country_title"/>
		<xsl:variable name="hotels_set" select="key('country_plus_city_hotels',concat(string($country_title),string(attribute::title)))"/>
			<xsl:element name="city">
				<xsl:attribute name="title">
					<xsl:value-of select="attribute::title"/>
				</xsl:attribute>

				<xsl:element name="hoteltypes">
					<xsl:for-each select="/./descendant::description[not(attribute::hoteltype=preceding::description/attribute::hoteltype)]/attribute::hoteltype">
						<xsl:variable name="type" select="."/>
						<xsl:element name="hoteltype">
							<xsl:attribute name="title"><xsl:value-of select="$type"/></xsl:attribute>
							<xsl:element name="hotels">
								<xsl:apply-templates select="$hotels_set[child::hotelinfo/description[attribute::hoteltype=$type]]"/>
							</xsl:element>
						</xsl:element>
					</xsl:for-each>
				</xsl:element>
				
			</xsl:element>
	</xsl:template>

	<xsl:template match="hotel">
		<xsl:element name="hotel">
			<xsl:attribute name="category">
				<xsl:value-of select="hotelinfo/description/attribute::category"/>
			</xsl:attribute>
			<xsl:attribute name="title">
				<xsl:value-of select="attribute::title"/>
			</xsl:attribute>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>
Еще поработаю над усовершенствованием.
...
Рейтинг: 0 / 0
22.06.2010, 23:58
    #36701936
y-niko
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Да, еще вот с выводом только не пустых типов отелей (изменился только один template):
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
	<xsl:template match="city">
		<xsl:param name="country_title"/>
		<xsl:variable name="hotels_set" select="key('country_plus_city_hotels',concat(string($country_title),string(attribute::title)))"/>
			<xsl:element name="city">
				<xsl:attribute name="title">
					<xsl:value-of select="attribute::title"/>
				</xsl:attribute>

				<xsl:element name="hoteltypes">
					<xsl:for-each select="/./descendant::description[not(attribute::hoteltype=preceding::description/attribute::hoteltype)]/attribute::hoteltype">
						<xsl:variable name="type" select="."/>
						<xsl:if test="count($hotels_set[child::hotelinfo/description[attribute::hoteltype=$type]])>0">
							<xsl:element name="hoteltype">
								<xsl:attribute name="title"><xsl:value-of select="$type"/></xsl:attribute>
								<xsl:element name="hotels">
									<xsl:apply-templates select="$hotels_set[child::hotelinfo/description[attribute::hoteltype=$type]]"/>
								</xsl:element>
							</xsl:element>
						</xsl:if>
					</xsl:for-each>
				</xsl:element>
				
			</xsl:element>
	</xsl:template>
...
Рейтинг: 0 / 0
23.06.2010, 14:16
    #36703317
Andry Trushin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
y-niko,

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

"академическое" написание осей это хорошо. но это теория.

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

оси child и attribute не используются почти никогда. (кстати странно, что вы не воспользовались тут "/./" осью parent)

<xsl:element> - используют для создания элементов с неопределенным именем, если имя константа он не нужен.

<xsl:attribute> - используют в случае мерцающего атрибута (в зависимости от условия или он есть или нет), либо для сложного контента, когда нужна проверка, тогда xsl:attribute используют как контейнер для choose.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<xsl:template match="city">
    <xsl:param name="country_title"/>
    <xsl:variable name="hotels_set" select="key('country_plus_city_hotels',concat($country_title,@title))"/>

	<city title="{@title}">
		<hoteltypes>
			<xsl:for-each select="/./descendant::description[not(@hoteltype=preceding::description/@hoteltype)]/@hoteltype">
				<xsl:variable name="type" select="."/>
				<xsl:if test="count($hotels_set[hotelinfo/description[@hoteltype=$type]])>0">
					<hoteltype title="{$type}">
						<hotels>
							<xsl:apply-templates select="$hotels_set[hotelinfo/description[@hoteltype=$type]]"/>
						</hotels>
					</hoteltype>
				</xsl:if>
			</xsl:for-each>
		</hoteltypes>
	</city>
</xsl:template>
...
Рейтинг: 0 / 0
23.06.2010, 14:20
    #36703330
Andry Trushin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
от переменной тоже можно избавится.

Код: plaintext
1.
2.
3.
4.
5.
6.
<xsl:if test="count($hotels_set[hotelinfo/description[@hoteltype=current()/text()]])>0">
	<hoteltype title="{text()}">
		<hotels>
			<xsl:apply-templates select="$hotels_set[hotelinfo/description[@hoteltype=current()/text()]]"/>
		</hotels>
	</hoteltype>
</xsl:if>
...
Рейтинг: 0 / 0
23.06.2010, 14:41
    #36703457
y-niko
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оглавление
Согласен, мой стиль описания "академичен" и безусловно его можно сократить. И должен заметить, что данный стиль был выбран мною умышленно и полностью осознанно.

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


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