XPath contains(text()、 'some string')は、複数のTextサブノードを持つノードで使用すると機能しません


259

Xpathにdom4jが含まれるという小さな問題があります...

私のXMLは

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

ルート要素を指定して、テキストにABCが含まれるすべてのノードを検索するとします。

だから私が書く必要があるxpathは

//*[contains(text(),'ABC')]

しかし、これはDom4jが返すものではありません....これはdom4jの問題ですか、それともxpathがどのように機能するかを理解しています。このクエリはStreet要素のみを返し、Comment要素は返しません。

DOMは、Comment要素を4つのタグ2

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

要素を見つけてそこに実行内容を含める必要があるため、クエリはまだ要素を返すはずですが、そうではありません... ...

次のクエリは要素を返しますが、要素だけではなく、親要素も返します...これは問題には望ましくありません...

//*[contains(text(),'ABC')]

要素<Street/>とのみを返すxpathクエリを知っている人はいます<Comment/>か?


私の知る//*[contains(text(),'ABC')]限り、<Street>要素のみを返します。<Street>またはの祖先を返しません<Comment>
Ken Bloom

回答:


707

<Comment>タグは、2つのテキスト・ノードと2つのが含ま<br>子としてノードを。

あなたのxpath式は

//*[contains(text(),'ABC')]

これを打破するために、

  1. * 任意の要素(タグなど)に一致するセレクタです。ノードセットを返します。
  2. []そのノードセット内の個々のノード上で動作する条件です。動作する個々のノードのいずれかが括弧内の条件に一致する場合に一致します。
  3. text()コンテキストノードの子であるすべてのテキストノードに一致するセレクタです。ノードセットを返します。
  4. contains文字列を操作する関数です。ノードセットが渡された場合、ノードセットは、ドキュメントセットの最初にあるノードセット内のノードの文字列値を返すことにより、文字列に変換されます。したがって、<Comment>要素の最初のテキストノードのみに一致できますBLAH BLAH BLAH。それが一致しないので<Comment>、結果にa を取得しません。

これを次のように変更する必要があります

//*[text()[contains(.,'ABC')]]
  1. * 任意の要素(タグなど)に一致するセレクタです。ノードセットを返します。
  2. 外部[]は、そのノードセット内の個々のノードで動作する条件付きです。ここでは、ドキュメント内の各要素で動作します。
  3. text()コンテキストノードの子であるすべてのテキストノードに一致するセレクタです。ノードセットを返します。
  4. 内部[]は、そのノードセットの各ノード(ここでは、個々のテキストノード)で動作する条件付きです。個々のテキストノードは、括弧内の任意のパスの開始点で.あり、括弧内として明示的に参照することもできます。動作する個々のノードのいずれかが括弧内の条件に一致する場合に一致します。
  5. contains文字列を操作する関数です。ここでは、個別のテキストノード(.)が渡されます。<Comment>タグの2番目のテキストノードが個別に渡されるため、'ABC'文字列が表示され、一致させることができます。

1
xpath noobの少し素晴らしいので、これを取得します。text()は、式contains(。、 'ABC')を使用する関数です。説明できる可能性があるので、このようなことはしません愚かなものを再び;)
マイクミルキン

28
私は長い説明を提供するために私の答えを編集しました。私自身はXPathについてあまり知りません-その組み合わせに出くわすまで、少しだけ実験しました。機能する組み合わせができたら、何が起こっているのかを推測し、XPath標準を調べて、何が起こっているのかを確認し、説明を書きました。
ケンブルーム

2
これをどのようにして大文字と小文字を区別しない検索にしますか?
ザック

@ザック:これを新しい質問にしてください。
user1129682 2015

1
私はこれが古いスレッドであることを知っていますが、根本的な違いがある場合は誰でもコメントできます。できれば、Ken Bloomとの回答の間にいくつかの簡単なテストケースを付けてください//*[contains(., 'ABC')]。私は常にマイクミルキンのパターンを使用して、より適切であると考えていcontainsましたが、現在のコンテキストで実行するだけの方が、実際にはもっと頻繁に必要なようです。
knickum 2015年

7

[contains(text(),'')]trueまたはfalseのみを返します。要素の結果は返されません。


''または ''があった場合、これは機能しません。
シャリーフ2018年

contains(text(),'JB-')動作しません!引数として2つの文字列conatains取ります- !text()はstringではなく、関数です!contains(**string**, **string**)
AtachiShadow

6

XMLドキュメント:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

XPath式:

//*[contains(text(), 'ABC')]

//*ルートノードの子孫要素に一致します。つまり、ルートノード以外のすべての要素です。

[...]ある述語が、それは、ノードセットをフィルタリングします。そのためには、ノードを返す...ですtrue

述語はノードセット[...]をフィルタリングして、新しいノードセットを生成します。フィルタリングするノードセットの各ノードについて、PredicateExprが評価されます[...]; PredicateExprがそのノードに対してtrueと評価された場合、ノードは新しいノードセットに含まれます。それ以外の場合は含まれません。

contains('haystack', 'needle')次を含むtrue場合haystack 返されますneedle

関数:boolean contains(string、string)

contains関数は、最初の引数文字列に2番目の引数文字列が含まれている場合はtrueを返し、それ以外の場合はfalseを返します。

ただしcontains()、最初のパラメータとして文字列を取ります。そして、それは渡されたノードです。最初のパラメーターとして渡されたすべてのノードまたはノードセットが関数によって文字列に変換されることを処理するにはstring()

引数は、文字列関数を呼び出した場合と同様に文字列型に変換されます。

string()関数リターンstring-value最初のノード

ドキュメントセットの最初にあるノードセット内のノードの文字列値を返すことにより、ノードセットは文字列に変換されます。ノードセットが空の場合、空の文字列が返されます。

string-value要素ノード

要素ノードの文字列値は、要素ノードのすべてのテキストノードの子孫の文字列値をドキュメント順に連結したものです。

string-valueテキストノード

テキストノードの文字列値は文字データです。

したがって、基本的にstring-valueはノードに含まれるすべてのテキストです(すべての子孫テキストノードの連結)。

text() 任意のテキストノードに一致するノードテストです。

ノードテストtext()は、どのテキストノードでもtrueです。たとえば、child :: text()は、コンテキストノードのテキストノードの子を選択します。

そうは言って//*[contains(text(), 'ABC')]も、最初のテキストノードにが含まれてABCいるすべての要素(ルートノードを除く)に一致します。以降text()戻りコンテキストノード(式が評価された相対的)のすべての子テキストノードを含むノードセット。しかしcontains()、最初のものだけを取ります。したがって、上記のドキュメントのパスはStreet要素とます。

次の式//*[text()[contains(., 'ABC')]]は、を含む少なくとも1つの子テキストノードを持つすべての要素(ルートノードを除く)に一致しますABC.コンテキストノードを表します。この場合は、ルートノード以外の任意の要素の子テキストノードです。したがって、上記のドキュメントのパスはと一致しStreetComment要素にます。

次に、//*[contains(., 'ABC')]ABC子孫テキストノードの連結に)を含むすべての要素(ルートノードを除く)を照合します。それ以上の文書が一致したためにHomeAddrStreet、およびComment要素を。このように、//*[contains(., 'BLAH ABC')]一致HomeAddrおよびComment要素。


0

少し時間がかかりましたが、ようやくわかりました。以下のテキストを含むカスタムxpathは、私にとっては完全に機能しました。

//a[contains(text(),'JB-')]

2
contains(text(),'JB-')動作しません!引数として2つの文字列conatains取ります- !text()はstringではなく、関数です!contains(**string**, **string**)
AtachiShadow

0

受け入れられた回答は、すべての親ノードも返します。文字列が後にあっても、ABCを持つ実際のノードのみを取得するには、次のようにします

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]

0
//*[text()='ABC'] 

戻り値

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>

3
5つの既存の回答を持つ9年前の質問に回答を追加する場合、回答が対応する質問のユニークな新しい側面を指摘することは非常に重要です。
Jason Aller

私が投稿した回答は非常に簡単でした。だから、私のような初心者を助けるかもしれない共有のように考えました。
user3520544
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.