Какое-то древнее дерьмо

Живёт тупо потому, что за хостинг уплачено на годы вперед

Генерация URL’ов в XSLT 1.0

13 декабря 2007 · Комментариев: 7 · XSLT/XPath, Рецептарий

В первой версии XSLT самой слабой, пожалуй, остаётся работа со строками.

Наиболее часто проблемы из-за этого возникают с генерацией URL’ов. Допустим, мы имеем страницу с адресом:

http://example.com/index.xml?section=catalog&cat-id=10&item-id=1404&item-unique-key=809124

Нам нужно, скажем, сгенерировать три ссылки: на главную страницу категории (cat-id), главную страницу (section) и независимую от категории страницу итема (item-id, item-unique-key).

Способов много, я по-началу даже использовал substring, но потом ради разнообразия включил моск и набросал небольшой шаблончик. Открытием для хоть сколько-то опытных разработчиков он не будет, так что будем считать это очередным self-note.

В первую очередь нам нужно надыбать xml с параметрами запроса. В идеале что-то вроде:

<state name="request">
    <param name="section">catalog</param>
    <param name="cat-id">10</param>
    <param name="item-id">1404</param>
    <param name="item-unique-key">809124</param>
</state>

Готовый вариант стилей будет выглядеть вот так:

<xsl:variable name="request" select="/page/state[@name='request']"/>

<xsl:template match="/">
    <html>
        <head>
	    <title>Custom page name</title>
	</head>
	<body>
	    <div id="place-holder">
		<ul>
		    <li>
			<a>
			    <xsl:attribute name="href">
				<xsl:apply-templates select="$request">
				    <xsl:with-param name="exclude-params" select="'item-id|item-unique-key'"/>
				</xsl:apply-templates>
			    </xsl:attribute>
			    Ссылка на общую страницу категории
			</a>
		    </li>
		    <li>
			<a>
			    <xsl:attribute name="href">
				<xsl:apply-templates select="$request">
				    <xsl:with-param name="exclude-params" select="'cat-id'"/>
				</xsl:apply-templates>
			    </xsl:attribute>
			    Ссылка на страницу итема, безотносительно категории
			</a>
		    </li>
		    <li>
			<a>
			    <xsl:attribute name="href">
				<xsl:apply-templates select="$request">
				    <xsl:with-param name="exclude-params" select="'cat-id|item-id|item-unique-key'"/>
				</xsl:apply-templates>
			    </xsl:attribute>
			    Ссылка на главную страницу раздела
			</a>
		    </li>
		</ul>
	    </div>
        </body>
    </html>
</xsl:template>

<xsl:template match="state">
    <xsl:param name="exclude-params"/>
    <xsl:text>http://example.com/index.xml</xsl:text>
    <xsl:apply-templates select="param[not(contains($exclude-params,@name))]"/>
</xsl:template>

<xsl:template match="param[position()=1]">
    <xsl:text>?</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>=</xsl:text>
    <xsl:value-of select="."/>
</xsl:template>

<xsl:template match="param">
    <xsl:text>&</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>=</xsl:text>
    <xsl:value-of select="."/>
</xsl:template>

Разберём чуть подробнее:

<xsl:attribute name="href">
    <xsl:apply-templates select="$request" name="create-url">
        <xsl:with-param name="exclude-params" select="'cat-id|item-id|item-unique-key'"/>
    </xsl:apply-templates>
</xsl:attribute>

Здесь мы запускаем шаблон для генерации URL’а, передавая ему, какие параметры не хотим видеть в создаваемом запросе. Если этих параметров в текущем URL’e нет, ничего страшного не случится.

Ну и, собственно, единственная соль:

<xsl:template match="state">
    <xsl:param name="exclude-params"/>
    <xsl:text>http://example.com/index.xml</xsl:text>
    <xsl:apply-templates select="param[not(contains($exclude-params,@name))]"/>
</xsl:template>

Не матчим те параметры, которые мы передали как исключаемые. Ну и собственно генерируем URL:

<xsl:template match="param">
    <xsl:text>&</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>=</xsl:text>
    <xsl:value-of select="."/>
</xsl:template>

Исключительно простая штука, которая может сэкономить начинающим кучу времени и нервов. Правда нужно еще xml получать правильный, но про пинание программеров будет в следующий раз :)

Желающие могут заценить исходники: xml, xsl.

Теги: · ·

Комментариев: 7 ↓

  • keep

    Хороший стиль и красиво, особенно для 1.0.

    Думаю с 2.0 то же можно написать в пару XPath :) Если в ближайшее время доживу до блога, жди 2.0 ответ :)

  • veged

    Мне стиль не понравился =(
    * строки — это не стихия xsl, зачем писать ‘item-id|item-unique-key’ строкой если есть xpath
    * следующий кусок раскопипащен:
    <xsl:value-of select=”@name”/>
    <xsl:text>=</xsl:text>
    <xsl:value-of select=”.”/>
    * почему копипаст это плохо становится ясно при добавлении str:encode-uri() к выводу значений

  • admin

    veged, честно говоря, ни по одному из пунктов толком тебя не понял.
    * про xpath не понимаю сходу разницу. На мой взгляд, обычной строкой лучше читается в коде. Или речь о фильтрации прямо в select’е у apply?

    Про str:encode-uri проблему не понял. Не использовал почти.

  • KBA-KBA

    Сначала подумал и переписал шаблоны (оба) match="param"
    так (DRY):

    <xsl:template match="param" mode="value">
    <xsl:value-of select="@name"/>=<xsl:value-of select="."/>
    </xsl:template>

    <xsl:template match="param[position()=1]">
    <xsl:text>?</xsl:text>
    <xsl:apply-templates select="." mode="value"/>
    </xsl:template>

    <xsl:template match="param[position()&gt;1]">
    <xsl:text>&amp;</xsl:text>
    <xsl:apply-templates select="." mode="value"/>
    </xsl:template>

    Потом подумал, что я бы написал так:

    <xsl:key name="href" match="/page/state" use="@name"/>

    <xsl:template match="/">
    <html>
    <head>
    <title>Custom page name</title>
    </head>
    <body>
    <div id="place-holder">
    <p>Текущий URL: http://example.com/index.xml?section=catalog&amp;cat-id=10&amp;item-id=1404&amp;item-unique-key=809124</p>
    <ul>
    <li>
    <a>
    <xsl:attribute name="href">
    <xsl:apply-templates select="key('href','request')">
    <xsl:with-param name="params">
    <xsl:apply-templates select="key('href','request')/param[not(contains('item-id|item-unique-key',@name))]" mode="value"/> </xsl:with-param>
    </xsl:apply-templates>
    </xsl:attribute>
    Ссылка на общую страницу категории
    </a>
    </li>
    <li>
    <a>
    <xsl:attribute name="href">
    <xsl:apply-templates select="key('href','request')">
    <xsl:with-param name="params">
    <xsl:apply-templates select="key('href','request')/param[@name='cat-id']" mode="value"/>
    </xsl:with-param>
    </xsl:apply-templates>
    </xsl:attribute>
    Ссылка на страницу итема, безотносительно категории
    </a>
    </li>
    </ul>
    </div>
    </body>
    </html>
    </xsl:template>

    <xsl:template match="state">
    <xsl:param name="params" select="''"/>
    <xsl:text>http://example.com/index.xml</xsl:text><xsl:if test="string-length($params)">?<xsl:value-of select="$params"/></xsl:if>
    </xsl:template>

    <xsl:template match="param" mode="value">
    <xsl:if test="position()&gt;1"><xsl:text>&amp;</xsl:text></xsl:if><xsl:value-of select="@name"/>=<xsl:value-of select="."/>
    </xsl:template>

    (поправил комментарий, было не понятно включен HTML или нет)

  • admin

    Вполне резонно. Но мне не нравится читаемость такого варианта.

  • KBA-KBA

    Согласен. Это плата за гибкость

  • keep

    читаемость прежде всего: никто не увидит гибкости в непонятном коде.

Оставить комментарий