如何使用XSLT/XPath生成逗号分隔的列表?

时间:2023-02-13 03:42:52

Given this XML data:

鉴于这个XML数据:

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

I can use this XSLT markup:

我可以使用这个XSLT标记:

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

to get this result:

得到结果:

apple, orange, banana,

but how do I produce a list where the last comma is not present? I assume it can be done doing something along the lines of:

但是如何生成一个没有最后一个逗号的列表呢?我认为它可以做一些类似的事情:

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

but what should the test expression be?

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

I need some way to figure out how long the list is and where I currently am in the list, or, alternatively, if I am currently processing the last element in the list (which means I don't care how long it is or what the current position is).

我需要某种方法来计算列表的长度和我当前在列表中的位置,或者,如果我正在处理列表中的最后一个元素(这意味着我不关心列表的长度或当前位置)。

7 个解决方案

#1


25  

Take a look at the position(), count() and last() functions; e.g., test="position() &lt; last()".

查看position()、count()和last()函数;例如,测试= "位置()& lt;最后()”。

#2


50  

This is a pretty common pattern:

这是一个很常见的模式:

<xsl:for-each select="*">
   <xsl:value-of select="."/>
   <xsl:if test="position() != last()">
      <xsl:text>,</xsl:text>
   </xsl:if>
</xsl:for-each>

#3


11  

For an XSLT 2.0 option, you can use the separator attribute on xsl:value-of.

对于XSLT 2.0选项,可以使用xsl:value-of上的分隔符属性。

This xsl:value-of:

这个xsl:作用是:

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

would produce this output:

会产生该输出:

apple, orange, banana

You could also use more than just a comma for a separator. For example, this:

您还可以对分隔符使用不止一个逗号。例如,这个:

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

Would produce the following output:

将产生以下输出:

'apple', 'orange', 'banana'

Another XSLT 2.0 option is string-join()...

另一个XSLT 2.0选项是string-join()……

<xsl:value-of select="string-join(/*/item,', ')"/>

#4


9  

<xsl:if test="following-sibling::*">,</xsl:if>

or (perhaps more efficient, but you'd have to test):

或者(也许效率更高,但你必须进行测试):

<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  

Robert gave the classis not(position() = last()) answer. This requires you to process the whole current node list to get context size, and in large input documents this might make the conversion consume more memory. Therefore, I normally invert the test to be the first thing

罗伯特给了班上的学生不及格的答案。这需要您处理整个当前节点列表以获得上下文大小,在大型输入文档中,这可能会使转换消耗更多的内存。因此,我通常把测试反演成第一件事

<xsl:for-each select="*">
  <xsl:if test="not(position() = 1)>, </xsl:if>
  <xsl:value-of select="."/>   
</xsl:for-each>

#6


6  

A simple XPath 1.0 one-liner:

简单的XPath 1.0一行代码:

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

concat()。,子字符串(',',2 - (position() != last()))))

Put it into this transformation:

将其转化为:

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

and apply it to the XML document:

并将其应用于XML文档:

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

to get the wanted result:

要得到想要的结果:

apple,orange,banana

苹果,橘子,香蕉

EDIT:

编辑:

Here is a comment from Robert Rossney to this answer:

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

That's pretty opaque code for a human to read. It requires you to know two non-obvious things about XSLT: 1) what the substring function does if its index is out of range and 2) that logical values can be implicitly converted to numerical ones.

对于人类来说,这是相当不透明的代码。它要求您了解关于XSLT的两件不明显的事情:1)如果子字符串函数的索引超出范围,它做什么;2)逻辑值可以隐式地转换为数值。

and here is my answer:

我的回答是:

Guys, never shy from learning something new. In fact this is all Stack Overflow is about, isn't it? :)

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

#7


2  

This is the way I got it working for me. I tested this against your list:

这就是我让它为我工作的方式。我测试了你的列表:

<?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>

#1


25  

Take a look at the position(), count() and last() functions; e.g., test="position() &lt; last()".

查看position()、count()和last()函数;例如,测试= "位置()& lt;最后()”。

#2


50  

This is a pretty common pattern:

这是一个很常见的模式:

<xsl:for-each select="*">
   <xsl:value-of select="."/>
   <xsl:if test="position() != last()">
      <xsl:text>,</xsl:text>
   </xsl:if>
</xsl:for-each>

#3


11  

For an XSLT 2.0 option, you can use the separator attribute on xsl:value-of.

对于XSLT 2.0选项,可以使用xsl:value-of上的分隔符属性。

This xsl:value-of:

这个xsl:作用是:

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

would produce this output:

会产生该输出:

apple, orange, banana

You could also use more than just a comma for a separator. For example, this:

您还可以对分隔符使用不止一个逗号。例如,这个:

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

Would produce the following output:

将产生以下输出:

'apple', 'orange', 'banana'

Another XSLT 2.0 option is string-join()...

另一个XSLT 2.0选项是string-join()……

<xsl:value-of select="string-join(/*/item,', ')"/>

#4


9  

<xsl:if test="following-sibling::*">,</xsl:if>

or (perhaps more efficient, but you'd have to test):

或者(也许效率更高,但你必须进行测试):

<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  

Robert gave the classis not(position() = last()) answer. This requires you to process the whole current node list to get context size, and in large input documents this might make the conversion consume more memory. Therefore, I normally invert the test to be the first thing

罗伯特给了班上的学生不及格的答案。这需要您处理整个当前节点列表以获得上下文大小,在大型输入文档中,这可能会使转换消耗更多的内存。因此,我通常把测试反演成第一件事

<xsl:for-each select="*">
  <xsl:if test="not(position() = 1)>, </xsl:if>
  <xsl:value-of select="."/>   
</xsl:for-each>

#6


6  

A simple XPath 1.0 one-liner:

简单的XPath 1.0一行代码:

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

concat()。,子字符串(',',2 - (position() != last()))))

Put it into this transformation:

将其转化为:

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

and apply it to the XML document:

并将其应用于XML文档:

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

to get the wanted result:

要得到想要的结果:

apple,orange,banana

苹果,橘子,香蕉

EDIT:

编辑:

Here is a comment from Robert Rossney to this answer:

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

That's pretty opaque code for a human to read. It requires you to know two non-obvious things about XSLT: 1) what the substring function does if its index is out of range and 2) that logical values can be implicitly converted to numerical ones.

对于人类来说,这是相当不透明的代码。它要求您了解关于XSLT的两件不明显的事情:1)如果子字符串函数的索引超出范围,它做什么;2)逻辑值可以隐式地转换为数值。

and here is my answer:

我的回答是:

Guys, never shy from learning something new. In fact this is all Stack Overflow is about, isn't it? :)

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

#7


2  

This is the way I got it working for me. I tested this against your list:

这就是我让它为我工作的方式。我测试了你的列表:

<?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>