Как использовать XSLT для создания разных значений - PullRequest
51 голосов
/ 18 февраля 2010

У меня есть 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>

Ответы [ 6 ]

51 голосов
/ 19 февраля 2010

Решение 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>
47 голосов
/ 19 сентября 2013

Вот решение 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>
17 голосов
/ 18 февраля 2010

Вам не нужен «вывод (однозначные значения)», а скорее «для каждого (отличные значения)»:

<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>
13 голосов
/ 02 апреля 2014

Я пришел к этой проблеме, работая с рендерингом 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>
8 голосов
/ 18 февраля 2010

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

0 голосов
/ 26 апреля 2019

Я обнаружил, что вы можете делать что хотите с 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>

Вы можете попробовать на игровой площадке .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...