powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / Оглавление
10 сообщений из 10, страница 1 из 1
Оглавление
    #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
Оглавление
    #36588685
cavalero
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сорри, я, конечно не хочу, чтобы кто-то за меня это сделал. Просто нужен совет от людей разбирающихся в вопросе в каком направлении двигаться.

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

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

если тебе еще интересно "как правильно", найди на википедии статью про XSL, там есть мой пример с xsl:key.
...
Рейтинг: 0 / 0
Оглавление
    #36691589
walek
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Можете посмотреть как делал я
http://valiullin.livejournal.com/13651.html
...
Рейтинг: 0 / 0
Оглавление
    #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
Оглавление
    #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
Оглавление
    #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
Оглавление
    #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
Оглавление
    #36703457
y-niko
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Согласен, мой стиль описания "академичен" и безусловно его можно сократить. И должен заметить, что данный стиль был выбран мною умышленно и полностью осознанно.

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


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