代码之家  ›  专栏  ›  技术社区  ›  Anders Sandvig

如何用xslt/xpath生成逗号分隔的列表?

  •  29
  • Anders Sandvig  · 技术社区  · 16 年前

    给定此XML数据:

    <root>
      <item>apple</item>
      <item>orange</item>
      <item>banana</item>
    </root>
    

    我可以使用此XSLT标记:

    ...
    <xsl:for-each select="root/item">
      <xsl:value-of select="."/>,
    </xsl:for-each>
    ...
    

    要获得此结果:

    苹果,橘子,香蕉,

    但是如何生成最后一个逗号不存在的列表呢?我认为它可以按照以下方式完成:

    ...
    <xsl:for-each select="root/item">
      <xsl:value-of select="."/>
      <xsl:if test="...">,</xsl:if>
    </xsl:for-each>
    ...
    

    但是测试表达式应该是什么呢?

    我需要一些方法来计算列表的长度和我当前在列表中的位置,或者,如果我当前正在处理列表中的最后一个元素(这意味着我不在乎它有多长或当前位置是什么)。

    7 回复  |  直到 8 年前
        1
  •  25
  •   Willie Wheeler    16 年前

    看看 position() , count() last() 功能;例如, test="position() &lt; last()" .

        2
  •  52
  •   Luc Hermitte    12 年前

    这是一个非常常见的模式:

    <xsl:for-each select="*">
       <xsl:value-of select="."/>
       <xsl:if test="position() != last()">
          <xsl:text>,</xsl:text>
       </xsl:if>
    </xsl:for-each>
    
        3
  •  11
  •   Daniel Haley    8 年前

    对于XSLT2.0选项,可以使用 separator 属性对 xsl:value-of .

    这个 XSL的价值 :

    <xsl:value-of select="/root/item" separator=", "/>
    

    将产生此输出:

    apple, orange, banana
    

    您还可以使用不仅仅是逗号作为分隔符。例如,这:

    <xsl:text>'</xsl:text>
    <xsl:value-of select="/root/item" separator="', '"/>
    <xsl:text>'</xsl:text>
    

    将产生以下输出:

    'apple', 'orange', 'banana'
    

    另一个XSLT2.0选项是 string-join()

    <xsl:value-of select="string-join(/*/item,', ')"/>
    
        4
  •  9
  •   Marc Gravell    16 年前
    <xsl:if test="following-sibling::*">,</xsl:if>
    

    或者(也许更有效,但你必须测试一下):

    <xsl:for-each select="*[1]">
       <xsl:value-of select="."/>
       <xsl:for-each select="following-sibling::*">
           <xsl:value-of select="concat(',',.)"/>
       </xsl:for-each>
    </xsl:for-each>
    
        5
  •  7
  •   Dimitre Novatchev    16 年前

    一个简单的xpath 1.0一行程序 :

    concat(., substring(',', 2 - (position() != last())))

    把它转化为:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>
    
        <xsl:template match="/*">
          <xsl:for-each select="*">
            <xsl:value-of select=
             "concat(., substring(',', 2 - (position() != last())))"
             />
          </xsl:for-each>
        </xsl:template>
    </xsl:stylesheet>
    

    并将其应用于XML文档 :

    <root>
        <item>apple</item>
        <item>orange</item>
        <item>banana</item>
    </root>
    

    要获得想要的结果:

    apple,orange,banana

    编辑:

    以下是罗伯特·罗斯尼对这个答案的评论:

    对于一个人来说,这是非常不透明的代码 阅读。你需要知道两个 关于xslt的不明显的事情:1)什么 如果子字符串函数 索引超出范围,2)该 逻辑值可以隐式 转换成数字的。

    这是我的答案 :

    伙计们,不要羞于学习新东西。实际上,这就是堆栈溢出的全部内容,是吗?:)

        6
  •  7
  •   jelovirt    16 年前

    罗伯特给了全班同学 not(position() = last()) 回答。这要求您处理整个当前节点列表以获取上下文大小,在大型输入文档中,这可能会使转换消耗更多内存。因此,我通常将测试倒置为第一件事

    <xsl:for-each select="*">
      <xsl:if test="not(position() = 1)>, </xsl:if>
      <xsl:value-of select="."/>   
    </xsl:for-each>
    
        7
  •  2
  •   Louise Rogow Howard    10 年前

    这就是我让它为我工作的方式。 我根据你的清单测试了这个:

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    
    <xsl:template match="root">
        <xsl:call-template name="comma-join"><xsl:with-param name="list" select="item"/></xsl:call-template>
    </xsl:template>
    
    <xsl:template name="comma-join">
        <xsl:param name="list" />
        <xsl:for-each select="$list">
            <xsl:value-of select="." />
            <xsl:if test="position() != last()">
                <xsl:text>, </xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:template> 
    </xsl:stylesheet>