XPath содержит (text(), 'some string') не работает при использовании с node с несколькими текстовыми поднаборами

У меня небольшая проблема с 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 Element, а не элемент Comment.

DOM делает элемент Comment составным элементом с четырьмя двумя тэгами

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

Я бы предположил, что запрос все равно должен возвращать элемент, так как он должен найти элемент, а run содержит на нем, но он не......

следующий запрос возвращает элемент, но он возвращает гораздо больше, чем просто элемент, он также возвращает родительские элементы... что нежелательно для проблемы...

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

Кто-нибудь знает запрос xpath, который возвращает только Элементы <Street/> и <Comment/>?

Ответ 1

Тег <Comment> содержит два текстовых узла и два <br> узла в качестве дочерних элементов.

Ваше выражение xpath было

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

Чтобы сломать это,

  • * - это селектор, который соответствует любому элементу (т.е. тегу) - он возвращает node -set.
  • [] - это условие, которое работает с каждым отдельным node в этом наборе node. Он соответствует, если какой-либо из отдельных узлов, с которыми он работает, соответствует условиям внутри скобок.
  • text() - селектор, который соответствует всем текстовым узлам, являющимся дочерними элементами контекста node - он возвращает набор node.
  • contains - это функция, которая работает с строкой. Если он передан набор node, набор node преобразуется в строку, возвращая строковое значение node в node > -set, который является первым в порядке документа. Следовательно, он может соответствовать только первому тексту node в вашем элементе <Comment>, а именно BLAH BLAH BLAH. Поскольку это не соответствует, вы не получите <Comment> в своих результатах.

Вам нужно изменить это на

//*[text()[contains(.,'ABC')]]
  • * - это селектор, который соответствует любому элементу (т.е. тегу) - он возвращает node -set.
  • Внешний [] является условным, который работает с каждым отдельным node в этом наборе node - здесь он работает с каждым элементом документа.
  • text() - селектор, который соответствует всем текстовым узлам, являющимся дочерними элементами контекста node - он возвращает набор node.
  • Внутренний [] является условным, который работает на каждом node в этом наборе node - здесь каждый отдельный текст node. Каждый отдельный текст node является отправной точкой для любого пути в скобках и также может быть явно обозначен как . в скобках. Он соответствует, если какой-либо из отдельных узлов, с которыми он работает, соответствует условиям внутри скобок.
  • contains - это функция, которая работает с строкой. Здесь он передается отдельным текстом node (.). Так как он передается второй текст node в теге <Comment> отдельно, он увидит строку 'ABC' и сможет ее сопоставить.

Ответ 2

[contains(text(),'')] возвращает только true или false. Он не будет возвращать результаты каких-либо элементов.

Ответ 3

Это заняло у меня немного времени, но, наконец, понял. Пользовательский xpath, содержащий текст ниже, работал для меня идеально.

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

Ответ 4

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:

Функция: логическое содержит (строка, строка)

Функция содержит возвращает истину, если первая строка аргумента содержит вторую строку аргумента, а в противном случае возвращает ложь.

Но contains() принимает строку в качестве первого параметра. И это прошло узлы. Чтобы справиться с этим, каждый узел или набор узлов, переданный в качестве первого параметра, преобразуется в строку с помощью функции string():

Аргумент преобразуется в тип 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')]] соответствует любому элементу (кроме корневого узла), который имеет хотя бы один дочерний текстовый узел, который содержит ABC. . представляет узел контекста. В этом случае это дочерний текстовый узел любого элемента, кроме корневого узла. Таким образом, для документа выше путь соответствует Street и Comment.

Теперь //*[contains(., 'ABC')] соответствует любому элементу (кроме корневого узла), который содержит ABC (в конкатенации нисходящих текстовых узлов). Для документа выше он соответствует элементам Home, Addr, Street и Comment. Таким образом, //*[contains(., 'BLAH ABC')] соответствует элементам Home, Addr и Comment.