web-dev-qa-db-ja.com

XSLTでは、異なるスコープからグローバル変数をどのようにインクリメントできますか?

ノードの数をカウントしたいXMLファイルを処理しているので、新しいノードを作成するときにIDとして使用できます。

現時点では、「counter」というグローバル変数があります。テンプレート内でアクセスできますが、テンプレート内で操作する方法が見つかりませんでした。

XSLTファイルの要約版は次のとおりです。

<xsl:variable name="counter" select="1" as="xs:integer"/>

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"></xsl:call-template>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">

   <!-- Increment 'counter' here -->

   <span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>

ここから行く方法はありますか?

27
Marcel

他の人は、変数がどのように不変であるかをすでに説明しています-XSLTには代入ステートメントがないこと(一般に純粋に機能的なプログラミング言語の場合と同様)。

これまでに提案されたソリューションの代替手段があります。パラメーターの受け渡しを回避します(XSLTでは冗長で見苦しいです。私も認めます)。

XPathでは、現在の要素の前にある<section>要素の数を単純にカウントできます。

<xsl:template name="section">
  <span class="title" id="title-{1 + count(preceding-sibling::section)}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

(注:空白のみのテキストノードはスタイルシートから自動的に削除されるため、空白コードの書式設定は結果に表示されません。そのため、同じ行に命令を配置する必要はありません。)

position()を使用するのとは対照的に)このアプローチの1つの大きな利点は、現在のノードリストではなく、現在のノードのみに依存することです。何らかの方法で処理を変更した場合(たとえば、<xsl:for-each>がセクションだけでなく他の要素も処理した場合)、position()の値はドキュメントの<section>要素の位置に必ずしも対応しなくなります。一方、上記のようにcount()を使用すると、常に各<section>要素の位置に対応します。このアプローチは、コードの他の部分との結合を減らします。これは一般的に非常に良いことです。

Count()の代わりに<xsl:number>命令を使用することもできます。デフォルトの動作では、同じ名前のすべての要素に同じレベルの番号が付けられます。

<xsl:template name="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

これは冗長性のトレードオフです(属性値テンプレートの中括弧を引き続き使用する場合は追加の変数宣言が必要です)が、XPath式も大幅に簡素化されるため、ほんの少しだけです。

まだ改善の余地があります。現在のノードリストへの依存関係を削除しましたが、まだ現在のノードに依存しています。それ自体は悪いことではありませんが、現在のノードが何であるかをテンプレートで確認してもすぐにはわかりません。わかっているのは、テンプレートの名前が「section」であることだけです。何が処理されているかを確実に知るには、コードの別の場所を調べる必要があります。しかし、そうである必要はありません。

(例のように)<xsl:for-each><xsl:call-template>を一緒に使用することになったと感じた場合は、戻って、代わりに<xsl:apply-templates>を使用する方法を見つけてください。

<xsl:template match="/doc">
  <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

このアプローチは冗長性が低い(<xsl:apply-templates/><xsl:for-each><xsl:call-template/>の両方を置き換える)だけでなく、現在のノードが何であるかがすぐに明らかになります。 match属性を調べるだけで、<section>要素を処理しており、<section>要素がカウントしていることがすぐにわかります。

テンプレートルール(つまり、match属性を持つ<xsl:template>要素)の仕組みの簡潔な説明については、 "XSLTの仕組み" を参照してください。

44
Evan Lenz

XSLT変数は変更できません。テンプレートからテンプレートに値を渡す必要があります。

XSLT 2.0を使用している場合、パラメーターを設定し、トンネリングを使用して変数を適切なテンプレートに伝播できます。

テンプレートは次のようになります。

<xsl:template match="a">
<xsl:param name="count" select="0">
  <xsl:apply-templates>
     <xsl:with-param select="$count+1"/>
  </xsl:apply-templates>
</xsl:template>

IDを作成する場合は、generate-id()の使用も参照してください。

9
BeWarned

XSLTの変数は不変であるため、それを念頭に置いて問題にアプローチする必要があります。 position()を直接使用できます:

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"/>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>

または、よりテンプレート指向の方法で:

<xsl:template match="/"> 
   <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
6
jelovirt

変数はローカルにスコープされ、xsltでのみ読み取ります。

2
Luixv

XSLTプロセッサーによっては、スクリプト化された関数をXLSTに導入できる場合があります。たとえば、Microsoft XMLライブラリはjavascriptの組み込みをサポートしています。例については、 http://msdn.Microsoft.com/en-us/library/aa970889(VS.85).aspx を参照してください。パブリッククライアントブラウザーでXSLTを展開/実行することを計画している場合、この戦術は明らかに機能しません。特定のXSLTプロセッサで実行する必要があります。

2

Position()関数を使用して、必要な処理を実行できます。これは次のようになります。

<xsl:template match="/">
  <xsl:for-each select="section">
    <xsl:call-template name="section">
      <xsl:with-param name="counter" select="{position()}"/>
    </xsl:call-template>
  </xsl:for-each>
</xsl:template>

<xsl:template name="section">
  <xsl:param name="counter"/>
  <span class="title" id="title-{$counter}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>
1

<xsl:variable name="RowNum" select="count(./preceding-sibling::*)" />および$ RowNumを増分値として使用します。

例:<xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

これにより、tile1、tile2、tile3などの値を持つリンクのクラスが作成されます。

0
marika.daboja

自分で試したことはありませんが、テンプレートにパラメータを渡してみてください。最初のテンプレートでは、for-eachステートメント内でパラメーターをcount()(またはcurrent()多分?)に設定し、その値を「セクション」テンプレートに渡します。

テンプレートにパラメーターを渡す の詳細

0
autonomatt