web-dev-qa-db-ja.com

XSLT / XPathを使用してコンマ区切りのリストを生成するにはどうすればよいですか?

このXMLデータを考えると:

 <root> 
 <item> Apple </ item> 
 <item> orange </ item> 
 <item>バナナ</ 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> 
 ... 

しかし、テスト式はどうあるべきですか?

リストの長さと現在リストのどこにいるかを把握する方法が必要です。あるいは、リストの最後の要素を現在処理している場合(つまり、長さや内容は気にしません)現在の位置です)。

29
Anders Sandvig

position()count()last()関数を見てください。例:test="position() &lt; last()"

25
Willie Wheeler

これはかなり一般的なパターンです:

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

XSLT 2.0オプションの場合、_xsl:value-of_でseparator属性を使用できます。

この_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,', ')"/>
_
11
Daniel Haley
<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>
9
Marc Gravell

単純な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

編集:

これは、この回答に対するRobert Rossneyからのコメントです。

これは、人間が読むにはかなり不透明なコードです。 XSLTについて2つの自明ではないことを知っておく必要があります。1)部分文字列関数がそのインデックスが範囲外の場合に行うこと、および2)論理値を暗黙的に数値に変換できること。

そしてこれが私の答えです

みんな、新しいことを学ぶのをためらわないでください。実際、これがすべてのスタックオーバーフローの問題ですよね。 :)

7

ロバートはclassisにnot(position() = last())と答えました。これにより、コンテキストサイズを取得するために現在のノードリスト全体を処理する必要があり、大きな入力ドキュメントでは、変換により多くのメモリが消費される可能性があります。したがって、私は通常、最初にテストを反転させます

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

これは私がそれを私のために機能させる方法です。私はあなたのリストに対してこれをテストしました:

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