2010-02-18 54 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 – 2010-02-18 19:33:19

回答

47

一种XSLT 1.0使用溶液和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

这完全适用于在整个/命名空间下查找不同的元素。如果目标是在<仓库名称=“a”><仓库名称=“b”>中找到cretain子树下的区别,那么全局密钥方法不再有效...... – 2014-07-28 16:08:27

+0

@R。 Simac - 您可以调整密钥的匹配表达式以匹配不同的项目集。 – 2014-07-29 00:37:46

+0

@Mads,我正在考虑无法设置密钥匹配“动态”。我不知道如何使用/指示键只匹配仓库X下的不同项目... – 2014-07-29 13:12:14

7

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

+0

@Tomalak,“指数“?否,XML文档中的元素节点和任何类型的叶节点数量中只有* linear *。 – 2012-10-11 22:01:11

+0

我不能得到这个工作,我的编译器(日食)抱怨说这是无效的XPath。 – Nicholas 2013-06-06 21:31:43

+2

@Nicholas这是针对XSLT 2.0的,但您正在使用XSLT 1.0处理器。你必须使用'',就像[接受的答案](http://stackoverflow.com/a/2293626/18771)那样。 – Tomalak 2013-06-07 06:35:49

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中存在一些错误....非常感谢... – 2010-02-18 20:19:13

+0

Tomalak xslt 2.0不受浏览器支持......只是在测试...时才来到knw ...任何方式在没有xslt 2.0的情况下做到这一点 – 2010-02-18 22:34:10

+0

@AB - 我已经添加了一个XSLT 1.0解决方案 – 2010-02-19 02:05:20

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限制不同的名称... – 2014-07-28 16:44:16

+0

真的@ R.Simac?下面的xpath应该给你的产品,第一个实例的名称(如果这是你想要的?)...'/ /产品[不((./名称=前面:: * /名称)]'。我相信它可能不适用于所有情况,也许你可以提供一个它不起作用的例子吗? – 2014-07-28 23:55:23

+0

@NickG ...这是'它不适用于我(tm)'的情况...例如,您的建议不会产生任何输出以下XML(对于丑陋的格式,时间限制抱歉): 膝上型 充电器 笔记本 耳机 充电器 2014-07-29 13:27:59

11

我来到这个问题与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> 
+1

这为我工作。能够将事情紧紧包裹在样式表中是很好的。添加取代允许我将模板应用于特定节点的值。 – kjl 2014-05-19 17:45:29

+0

适合我! Upvote给出。 – Naha 2017-05-22 11:38:33