Как использовать XSLT для создания отдельных значений
У меня такой XML:
<items>
<item>
<products>
<product>laptop</product>
<product>charger</product>
</products>
</item>
<item>
<products>
<product>laptop</product>
<product>headphones</product>
</products>
</item>
</items>
Я хочу, чтобы он выводился как
laptop
charger
headphones
Я пытался использовать distinct-values()
, но я думаю, что я делаю что-то неправильно. Может ли кто-нибудь сказать мне, как достичь этого, используя distinct-values()
? Спасибо.
<xsl:template match="/">
<xsl:for-each select="//products/product/text()">
<li>
<xsl:value-of select="distinct-values(.)"/>
</li>
</xsl:for-each>
</xsl:template>
но он дает мне вывод следующим образом:
<li>laptop</li>
<li>charger</li>
<li>laptop></li>
<li>headphones</li>
Ответы
Ответ 1
Решение XSLT 1.0, которое использует функции key
и generate-id()
для получения различных значений:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:key name="product" match="/items/item/products/product/text()" use="." />
<xsl:template match="/">
<xsl:for-each select="/items/item/products/product/text()[generate-id()
= generate-id(key('product',.)[1])]">
<li>
<xsl:value-of select="."/>
</li>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Ответ 2
Здесь решение XSLT 1.0, которое я использовал в прошлом, я считаю его более сжатым (и читаемым), чем использование функции generate-id()
.
<xsl:template match="/">
<ul>
<xsl:for-each select="//products/product[not(.=preceding::*)]">
<li>
<xsl:value-of select="."/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
Возврат:
<ul xmlns="http://www.w3.org/1999/xhtml">
<li>laptop</li>
<li>charger</li>
<li>headphones</li>
</ul>
Ответ 3
Вы не хотите "выводить (отдельные значения)", а скорее "для каждого (отдельные значения)":
<xsl:template match="/">
<xsl:for-each select="distinct-values(/items/item/products/product/text())">
<li>
<xsl:value-of select="."/>
</li>
</xsl:for-each>
</xsl:template>
Ответ 4
Я столкнулся с этой проблемой при работе с рендерингом Sitecore XSL. Оба подхода, которые использовали key() и подход, который использовал предыдущую ось, выполнялись очень медленно. Я закончил использование метода, аналогичного key(), но для этого не требуется использовать key(). Он работает очень быстро.
<xsl:variable name="prods" select="items/item/products/product" />
<xsl:for-each select="$prods">
<xsl:if test="generate-id() = generate-id($prods[. = current()][1])">
<xsl:value-of select="." />
<br />
</xsl:if>
</xsl:for-each>
Ответ 5
distinct-values(//product/text())
Ответ 6
Я обнаружил, что вы можете делать что хотите с XSLT 1.0 без функций generate-id()
и key()
.
Вот специфичное для Microsoft решение (класс .NET XslCompiledTransform, или MSXSLT.exe или COM-объекты Microsoft platfocm).
Это основано на этом ответе. Вы можете скопировать отсортированный узел в переменную ($sorted-products
в таблице стилей ниже), а затем преобразовать его в набор узлов с помощью функции ms:node-set
. Затем вы можете for-each
второй раз после сортировки набора узлов:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:ms="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="ms">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:variable name="sorted-products">
<xsl:for-each select="//products/product">
<xsl:sort select="text()" />
<xsl:copy-of select=".|@*" />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="products" select="ms:node-set($sorted-products)/product" />
<xsl:for-each select="$products">
<xsl:variable name='previous-position' select="position()-1" />
<xsl:if test="normalize-space($products[$previous-position]) != normalize-space(./text())">
<li>
<xsl:value-of select="./text()" />
</li>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
выход:
<li>charger</li>
<li>headphones</li>
<li>laptop</li>
Вы можете попробовать это на онлайн игровой площадке.