2010-02-18 2 views
46

У меня есть XML, как это:Как использовать XSLT для создания различных значений

<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> 
+0

Может быть стоит посмотреть на этот смежный вопрос: http://stackoverflow.com/questions/1813286/xslt-select-distinct-but-slightly-different-to-other-examples –

ответ

47

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> 
+0

Это отлично работает _only_ для поиска отдельных элементов под целым/пространством имен. Если цель состоит в том, чтобы найти отличия под подзаголовками cretain, как в <имя склада = "b">, тогда глобальный ключевой подход больше недействителен ... –

+0

@R. Simac - вы можете настроить выражение соответствия для ключа в соответствии с другим набором элементов. –

+0

@Mads, я думал о том, что не могу установить ключевое совпадение «динамически». Я не знаю, как использовать/инструктировать ключ, чтобы соответствовать только отдельным элементам под складом X ... –

7

distinct-values(//product/text())

+0

@Tomalak, «экспоненциальный «? Нет, только * linear * в количестве узлов элементов и в каждом узле листа любого типа в документе XML. –

+0

Я не могу заставить это работать, мой компилятор (eclipse) жалуется, что это неверно XPath. – Nicholas

+2

@Nicholas Это для XSLT 2.0, но вы работаете с процессором XSLT 1.0. Вы должны использовать '', как это сделал [принятый ответ] (http://stackoverflow.com/a/2293626/18771). – Tomalak

14

Вы не хотите "вывода (отличных стоимостей)", а «для-каждого (distinct- значения) ":

<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> 
+0

Его работоспособность ... в моем xml была какая-то ошибка ... спасибо большое ... –

+0

Tomalak xslt 2.0 не поддерживается браузером ... просто пришел в ногу ... во время тестирования ... любым способом сделать это без xslt 2.0 –

+0

@AB - Я добавил решение XSLT 1.0 –

45

Вот 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> 
+0

Оценивая ответ выше, который полностью применим для оригинального случая, просто хотелось бы отметить, что вышеупомянутый подход неприменим для немного более сложной схемы, где каждый продукт имеет свои собственные элементы , например: зарядное ноутбук ... Я не смог найти различные названия для такой компоновки, возможно, достигающего ограничения xslt1.0 здесь ... –

+0

Действительно @ R.Simac? Следующий xpath должен предоставить вам продукты с первым экземпляром имени (если это то, что вы хотите?) ... '// product [not (./ name = previous :: */name)]'. Я считаю, что он может не работать для всех сценариев, возможно, вы можете привести пример, где он не работает? –

+0

@NickG ... это была одна из таких ситуаций: «Это не работает для меня (tm)». Например, ваше предложение не выводит никаких результатов для следующих xml (извините за уродливое форматирование, ограничение по времени): ноутбук зарядное ноутбук наушники зарядное

11

Я пришел к этой проблеме во время работы с XSL рендеринга Sitecore. Оба подхода, которые использовали 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> 
+1

Это сработало для меня. Приятно было укладывать вещи в герметичную оболочку внутри таблицы стилей. Добавление вместо значения позволило мне применить шаблон только к определенному узлу. – kjl

+0

Работает для меня! Упрощение. – Naha

Смежные вопросы