6

無駄に遊んだ後position()、私は解決策を探し回っていて、私の問題をほぼ説明しているこの古いスタックオーバーフローの質問に到達しました。

違いは、位置を指定したいノードセットが、ドキュメントの連続したセクションではなく、動的であるということです。

説明のために、リンクされた質問の例を変更して、要件に一致させます。<b>各要素は異なる要素内にあることに注意してください<a>。これは重要なビットです。

<root>
    <a>
        <b>zyx</b>
    </a>
    <a>
        <b>wvu</b>
    </a>
    <a>
        <b>tsr</b>
    </a>
    <a>
        <b>qpo</b>
    </a>
</root>

ここで、クエリを実行すると、XPath fora/bを使用して、4つのノードのノードセットを取得し<b>ます。次に、文字列を含むノードのノードセット内の位置を見つけたいと思います'tsr'。他の投稿の解決策はここで分解されます:コンテキストノードセットではなくドキュメントをナビゲートしているためにcount(a/b[.='tsr']/preceding-sibling::*)+1戻ります。1preceding-sibling

コンテキストノードセット内で作業することは可能ですか?

4

6 に答える 6

5

以下は、同じドキュメント内の任意のノード セットに属する任意のノードで機能する一般的なソリューションです。

XSLT を使用してソリューションを実装していますが、最終的に他のホスティング言語で使用できる単一の XPath 式を取得します。

をノード$vNodeSetセットと$vNodeし、位置を見つけたいこのノードセット内のノードとします。

次に、 let$vPrecNodesには、 の前にある XML ドキュメント内のすべてのノードが含まれます$vNode

次に、 let$vAncNodesには、 の祖先である XML ドキュメント内のすべてのノードが含まれます$vNode

ドキュメント順$vNodeSetで前にある のノードのセットは、 にも属するノードセット内のすべてのノードと、 にも属するノードセット内のすべてのノードで構成されます。$vNode$vPrecNodes$vAncNodes

2 つのノードセットの交差には、よく知られているケイシアンの公式を使用します。

$ns1[count(.|$ns2) = count($ns2)]

$ns1との交点にあるノードを正確に含みます$ns2

これらすべてに基づいて、ドキュメントの順序で先行する$vPrecInNodeSetノードのセットをみましょう。次の XPath 式は を定義します。$vNodeSet$vNode$vPrecInNodeSet

$vNodeSet
      [count(.|$vPrecNodes) = count($vPrecNodes)
      or
       count(.|$vAncNodes) = count($vAncNodes)
      ]

最後に、必要な位置は次のとおりです。count($vPrecInNodeSet) +1

これがすべて一緒に機能する方法は次のとおりです。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:variable name="vNodeSet" select="/*/a/b"/>

 <xsl:variable name="vNode" select="$vNodeSet[. = 'tsr'][1]"/>

 <xsl:variable name="vPrecNodes" select="$vNode/preceding::node()"/>

 <xsl:variable name="vAncNodes" select="$vNode/ancestor::node()"/>

 <xsl:variable name="vPrecInNodeSet" select=
  "$vNodeSet
      [count(.|$vPrecNodes) = count($vPrecNodes)
      or
       count(.|$vAncNodes) = count($vAncNodes)
      ]
  "/>

 <xsl:template match="/">
   <xsl:value-of select="count($vPrecInNodeSet) +1"/>
 </xsl:template>
</xsl:stylesheet>

提供された XML ドキュメントに上記の変換を適用すると、次のようになります。

<root>
    <a>
        <b>zyx</b>
    </a>
    <a>
        <b>wvu</b>
    </a>
    <a>
        <b>tsr</b>
    </a>
    <a>
        <b>qpo</b>
    </a>
</root>

正しい結果が生成されます。

3

: このソリューションは XSLT に依存しません (説明目的でのみ使用されます)。置換する変数がなくなるまで、変数をその定義に置き換えて、1 つの XPath 式を組み立てることができます。

于 2010-08-25T03:45:12.230 に答える
2

私は実用的な解決策があると思います

アイデアは、ドキュメント内のターゲット要素の前にある要素の数を数え、前の要素の数が少ないか等しい数のノードセット内のノードの数を数えることです。XPathでは、これは次のとおりです。

count(//a/b[count(./preceding::node()) &lt;= count(//a/b[.='tsr']/preceding::node())])

この式で変数を使用して、さまざまなノードセットを検索したり、さまざまなテキストコンテンツを照合したりすることもできます。ここで重要なのは、変数の型が正しいことです。以下は、XSLTの例と、質問のサンプルドキュメントを入力ファイルとして使用した出力例です。

XSLTドキュメント

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output encoding="utf-8" method="text"/>

    <xsl:variable name="nodeset" select="//a/b"/>
    <xsl:variable name="path-string">//a/b</xsl:variable>
    <xsl:variable name="text">tsr</xsl:variable>

    <xsl:template match="/">
        <xsl:text>Find and print position of a node within a nodeset&#10;&#10;</xsl:text>

        <xsl:text>Position of "tsr" node in the nodeset = "</xsl:text>
        <xsl:value-of select="count(//a/b[count(./preceding::node()) &lt;= count(//a/b[.='tsr']/preceding::node()) ])"/>
        <xsl:text>"&#10;&#10;</xsl:text>

        <xsl:text>( Try the same using variables "$nodeset" and "$text" )&#10;</xsl:text>
        <xsl:text>Size of nodeset "$nodeset" = "</xsl:text>
        <xsl:value-of select="count($nodeset)"/>
        <xsl:text>"&#10;</xsl:text>
        <xsl:text>Variable "$text" = "</xsl:text>
        <xsl:value-of select="$text"/>
        <xsl:text>"&#10;</xsl:text>
        <xsl:text>Position of "</xsl:text>
        <xsl:value-of select="$text"/>
        <xsl:text>" node in the nodeset = "</xsl:text>
        <xsl:value-of select="count($nodeset[count(./preceding::node()) &lt;= count($nodeset[.=$text]/preceding::node()) ])"/>
        <xsl:text>"&#10;&#10;</xsl:text>

        <xsl:text>( Show that using a variable that has the path as a string does not work )&#10;</xsl:text>
        <xsl:text>Variable "$path-string" = "</xsl:text>
        <xsl:value-of select="$path-string"/>
        <xsl:text>"&#10;</xsl:text>
        <xsl:text>Result of "count($path-string)" = "</xsl:text>
        <xsl:value-of select="count($path-string)"/>
        <xsl:text>"&#10;&#10;</xsl:text>

        <xsl:text>End of tests&#10;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

サンプルドキュメントからの出力

Find and print position of a node within a nodeset

Position of "tsr" node in the nodeset = "3"

( Try the same using variables "$nodeset" and "$text" )
Size of nodeset "$nodeset" = "4"
Variable "$text" = "tsr"
Position of "tsr" node in the nodeset = "3"

( Show that using a variable that has the path as a string does not work )
Variable "$path-string" = "//a/b"
Result of "count($path-string)" = "1"

End of tests

私は自分のソリューションを広範囲にテストしていませんので、使用する場合はフィードバックをお寄せください。

于 2010-08-18T13:07:40.847 に答える
1

以前の count-the-preceding(-sibling) の回答は、場合によってはうまく機能します。選択したアイテムの観点からコンテキストノードセットを再指定してから、それに適用count(preceding:: )するだけです。

しかし、他の場合では、あなたがほのめかしていたように、操作したいノードセット内にcount-the-precedingを保持するのは本当に難しいです。たとえば、作業ノードセットが /html/body/div[3]//a ( Web ページ<a>の 3 番目のすべてのアンカー) であり、そのセット内<div>の位置を見つけたいとします。a[@href="foo.html"]を使用しようとすると、誤って他の div から、つまり作業中のノードセットの外からアンカーをcount(preceding::a)カウントしてしまいます。<a>を試しcount(preceding-sibling::a)ても、関連する<a>要素がどのレベルにもある可能性があるため、すべてを取得することはできません。

を使用してカウントを制限しようとすることもできますがpreceding::a[ancestor::div[count(preceding-sibling::div) = 2]]、非常に扱いにくくなり、すべての場合に可能とは限りません。さらに、作業セットの XPath 式を更新したことがある場合は、この式を作り直す必要があり、それらを同等に保つことは簡単ではありません。

ただし、XSLT を使用している場合は、次の方法でこれらの問題を回避できます。作業中のノードセットを指定できる場合は、指定された条件に一致するその中のノードの位置を見つけることができます。ノードセットを 2 回指定する必要はありません。

    <xsl:for-each select="/root/a/b">
        <xsl:if test=". = 'tsr'"><xsl:value-of select="position()"/></xsl:if>
    </xsl:for-each>

これが機能するのは、for-each 内で、コンテキスト位置が「処理中のシーケンス内のコンテキスト項目の位置を識別する」ためです。

XSLT を使用していない場合、どの環境にいますか? 外側の XPath 式の結果を反復処理するための同様の構造がおそらくそこにあり、そこで独自のカウンターを維持し (利用可能なコンテキスト位置がない場合)、内側の基準に対して各項目をテストできます。

古い質問,に対する他の人の試みがうまくいかなかった理由a/b[.='tsr']/position()は、スラッシュごとに新しいコンテキストがスタックにプッシュされるため、position() が呼び出されると、コンテキストの位置は常に 1 になるためです. (この構文はちなみに、XPath 2.0 でのみ動作します。)

于 2010-08-19T16:40:31.917 に答える
0

ルートから (つまり、ルートに対して):

count(//a/b[.='tsr']/preceding::b)

たとえば、別のノードを言っていた場合:

<c>
    <b>qqq</b>
</c>

そして、「a」の親を持たないすべての要素を無視したい場合は、次のようにすることができます

count(//a/b[.='tsr']/preceding::b[local-name(parent::node())='a'])

于 2010-07-28T15:01:20.060 に答える
0

1 を取得する理由は、コンテキストとドキュメントとは関係ありませんがb、1 つのノード内のノードのみをカウントしているaためです (そのため、先行する「b」ノードがないため、常に 0 のカウントが得られます。

むしろ、「a」を含む「b」の前に先行する「a」ノードの数を見つける必要があります。

何かのようなもの:

count(a[b[.='tsr']]/preceding-sibling::a)
于 2010-04-09T09:18:39.883 に答える
-1

これはどう..

count(a/b[.='tsr']/preceding-sibling::b) + count(a[b[.='tsr']]/preceding-sibling::a/b) + 1

現在の a 要素内の b 要素の前の兄弟を数え、次に a 要素の前のすべての兄弟の b 要素を数えます。またはそのようなもの。

于 2010-04-09T11:24:47.843 に答える