Подтвердить что ты не робот

Как создать список с разделителями-запятыми с помощью XSLT/XPath?

Учитывая данные 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>
...

но каково должно быть тестовое выражение?

Мне нужен способ выяснить, сколько времени осталось в списке и где я сейчас в списке, или, в качестве альтернативы, если я в настоящее время обрабатываю последний элемент в списке (что означает, что мне все равно, как долго есть или что такое текущая позиция).

4b9b3361

Ответ 1

Взгляните на функции position(), count() и last(); например, test="position() &lt; last()".

Ответ 2

Это довольно распространенный шаблон:

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

Ответ 3

Для параметра XSLT 2.0 вы можете использовать атрибут separator на xsl:value-of.

Это xsl:value-of:

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

Другой вариант XSLT 2.0 - string-join()...

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

Ответ 4

<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

Роберт дал ответ classis not(position() = last()). Это требует, чтобы вы обработали весь текущий список node, чтобы получить размер контекста, и в больших входных документах это может заставить конверсию потреблять больше памяти. Поэтому я обычно инвертирую тест, чтобы быть первым.

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

Ответ 6

Простой однострочный интерфейс 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

EDIT:

Вот комментарий Роберта Россни к этому ответу:

Этот довольно непрозрачный код для человека читать. Это требует, чтобы вы знали два неочевидные вещи о XSLT: 1) что функция подстроки, если ее индекс выходит за пределы диапазона и 2) что логические значения могут быть неявно преобразованные в числовые.

, и вот мой ответ:

Ребята, никогда не стесняйтесь изучать что-то новое. На самом деле это все переполнение стека, не так ли?:)

Ответ 7

Это то, как я заработал у меня. Я проверил это против вашего списка:

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