xpathを使用してノードの位置を見つける


86

xpathを使用してノードの位置を取得する方法を知っている人はいますか?

次のxmlがあるとします。

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

次のxpathクエリを使用して、3番目の<b>ノード(<b> tsr </ b>)を選択できます。

a/b[.='tsr']

これはすべてうまくいっていますが、次のようなノードの通常の位置を返したいと思います。

a/b[.='tsr']/position()

(しかし、もう少し機能します!)

それも可能ですか?

編集:.net 2を使用していることを忘れたので、xpath 1.0です!


更新JamesSulak優れた回答を使用することになりました。興味のある方のために、C#での私の実装を以下に示します。

int position = doc.SelectNodes("a/b[.='tsr']/preceding-sibling::b").Count + 1;

// Check the node actually exists
if (position > 1 || doc.SelectSingleNode("a/b[.='tsr']") != null)
{
    Console.WriteLine("Found at position = {0}", position);
}

問題の答えを投稿しないようにしてみてください- >それは、その後、その答えとしてこれを掲載している方が良いでしょう、おそらく質問から、それにリンクされています。
theMayer

回答:


94

試してみてください:

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

1
'Coz .netを使用していますが、それを使用しているか、使用した電力を処理できません:int position = doc.SelectNodes( "a / b [。=' tsr '] / preceding-Sibling :: b") .Count + 1; if(position> 1 || doc.SelectSingleNode( "a / b [。= 'tsr']")!= null)//ノードが実際に存在することを確認します{//ここでマジックを実行します}
Wilfred Knievel

インデックスがゼロの言語では、+ 1は必要ありません
JonnyRaa 2017年

9

これはXSLTで実行できますが、ストレートXPathについてはよくわかりません。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" encoding="utf-8" indent="yes" 
              omit-xml-declaration="yes"/>
  <xsl:template match="a/*[text()='tsr']">
    <xsl:number value-of="position()"/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

9

私はそのポストが古いことを理解しています..しかし..

アスタリスクをノード名に置き換えると、より良い結果が得られます

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

の代わりに

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

4

XPath 2.0にアップグレードする場合は、関数index-ofを提供することに注意してください。これにより、問題が次のように解決されます。

index-of(//b, //b[.='tsr'])

どこ:

  • 最初のパラメータは検索のシーケンスです
  • 2番目は検索するものです

これはXPath2 +でのみ機能することに注意してください。それ以下のものはすべて、「奇妙な」カウント関数を使用する必要があります。
ダンアトキンソン2015

1
@Dan、元のドキュメントへのリンクに記載されており、明示的な通知が追加されました。ありがとうございます。
CroWell 2015

3

前に述べたのとは異なり、「preceding-sibling」は実際に使用する軸であり、まったく異なることを行う「preceding」ではなく、現在のノードの開始タグの前にあるドキュメント内のすべてを選択します。(http://www.w3schools.com/xpath/xpath_axes.aspを参照


4
祖先ノードは含まれません。詳細についてはw3schoolsを信用しないでください!しかし、私は同意します...先行する::この場合は機能します。祖先以外の関連するb要素の前に要素がないため、先行する兄弟よりも脆弱です。OTOH、OPは、彼が内部の位置を知りたいコンテキストを教えてくれなかったので、潜在的に先行する::が正しい可能性があります。
LarsH 2010

2

JamesSulakによる回答へのメモ。

ノードが存在しない可能性があることを考慮し、それを純粋にXPATHのままにしておきたい場合は、ノードが存在しない場合に0を返す次のことを試してください。

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

0

問題は、ノードの位置はコンテキストなしではあまり意味がないということです。

次のコードは、親子ノード内のノードの場所を示します

using System;
using System.Xml;

public class XpathFinder
{
    public static void Main(string[] args)
    {
        XmlDocument xmldoc = new XmlDocument();
        xmldoc.Load(args[0]);
        foreach ( XmlNode xn in xmldoc.SelectNodes(args[1]) )
        {
            for (int i = 0; i < xn.ParentNode.ChildNodes.Count; i++)
            {
                if ( xn.ParentNode.ChildNodes[i].Equals( xn ) )
                {
                    Console.Out.WriteLine( i );
                    break;
                }
            }
        }
    }
}

1
つまり、現在は実際にはXPathファインダーではなく、C#ファインダーです。
jamesh 2008年

0

私はNovellIdentity Managerの作業をたくさん行っていますが、そのコンテキストでのXPATHは少し異なります。

探している値がTARGETという文字列変数にあるとすると、XPATHは次のようになります。

count(attr/value[.='$TARGET']/preceding-sibling::*)+1

さらに、数文字のスペースを節約するために、以下も同様に機能することが指摘されました。

count(attr/value[.='$TARGET']/preceding::*) + 1

また、NovellのCool Solutionsにこれのより美しいバージョンを投稿しました:XPATHを使用して位置ノードを取得します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.