XPath содержит (text(), "некоторая строка") не работает при использовании с узлом с более чем одним текстовым подузлом
У меня небольшая проблема с 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 составным элементом с четырьмя тегами два
[Text = 'XYZ'][BR][BR][Text = 'ABC']
Я бы предположил, что запрос все еще должен возвращать элемент, так как он должен найти элемент и выполнить на нем содержится, но он не......
следующий запрос возвращает элемент, но он возвращает гораздо больше, чем просто элемент, он также возвращает родительские элементы... что нежелательно для проблемы...
//*[contains(text(),'ABC')]
Кто-нибудь знает запрос xpath, который будет возвращать только элементы <Street/>
а также <Comment/>
?
9 ответов
<Comment>
тег содержит два текстовых узла и два <br>
узлы как дети.
Ваше выражение xpath было
//*[contains(text(),'ABC')]
Чтобы сломать это,
*
является селектором, который соответствует любому элементу (т. е. тегу) - он возвращает набор узлов.[]
являются условием, которое действует на каждый отдельный узел в этом наборе узлов. Он совпадает, если какой-либо из отдельных узлов, с которыми он работает, соответствует условиям внутри скобок.text()
является селектором, который соответствует всем текстовым узлам, которые являются дочерними элементами контекстного узла - он возвращает набор узлов.contains
это функция, которая работает со строкой Если ему передан набор узлов, набор узлов преобразуется в строку, возвращая строковое значение узла в наборе узлов, который находится первым в порядке документов. Следовательно, он может соответствовать только первому текстовому узлу в вашем<Comment>
элемент, а именноBLAH BLAH BLAH
, Поскольку это не соответствует, вы не получите<Comment>
в ваших результатах.
Вы должны изменить это на
//*[text()[contains(.,'ABC')]]
*
является селектором, который соответствует любому элементу (т. е. тегу) - он возвращает набор узлов.- Внешний
[]
являются условием, которое действует на каждый отдельный узел в этом наборе узлов - здесь оно действует на каждый элемент в документе. text()
является селектором, который соответствует всем текстовым узлам, которые являются дочерними элементами контекстного узла - он возвращает набор узлов.- Внутренний
[]
являются условием, которое действует на каждый узел в этом наборе узлов - здесь каждый отдельный текстовый узел. Каждый отдельный текстовый узел является отправной точкой для любого пути в скобках и может также явно указываться как.
в скобках. Он совпадает, если какой-либо из отдельных узлов, с которыми он работает, соответствует условиям внутри скобок. contains
это функция, которая работает со строкой Здесь передается отдельный текстовый узел (.
). Так как он передается второй текстовый узел в<Comment>
отметьте индивидуально, он увидит'ABC'
строка и быть в состоянии соответствовать.
Документ 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 для этого узла истинно, узел включается в новый набор узлов; в противном случае он не включается.
contains('haystack', 'needle')
возвращается true
если haystack
содержит needle
:
Функция: логическое значение содержит (строка, строка)
Функция contains возвращает истину, если первая строка аргумента содержит вторую строку аргумента, и в противном случае возвращает ложь.
Но contains()
принимает строку в качестве первого параметра. И это пройденные узлы. Чтобы справиться с этим, каждый узел или набор узлов, переданный в качестве первого параметра, преобразуется в строку с помощьюstring()
функция:
Аргумент преобразуется в строку типа, как если бы она вызывалась строковой функцией.
string()
функция возвращает string-value
из первого узла:
Набор узлов преобразуется в строку путем возврата строкового значения узла в наборе узлов, который является первым в порядке документа. Если набор узлов пуст, возвращается пустая строка.
string-value
из узла элемента:
Строковое значение узла элемента - это конкатенация строковых значений всех текстовых узлов-потомков узла элемента в порядке документа.
string-value
из текстового узла:
Строковое значение текстового узла - это символьные данные.
Итак, в основном string-value
- это весь текст, содержащийся в узле (объединение всех текстовых узлов-потомков).
text()
это тест узла, который соответствует любому текстовому узлу:
Текст проверки узла () верен для любого текстового узла. Например, 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
элементы.
Современный ответ, который охватывает поведение XPath 1.0 и XPath 2.0+...
Этот XPath,
//*[contains(text(),'ABC')]
ведет себя по-разному с XPath 1.0 и более поздними версиями XPath (2.0+).
Обычное поведение
-
//*
выделяет все элементы в документе. -
[]
фильтрует эти элементы в соответствии с выраженным в них предикатом. - внутри предиката отфильтрует эти элементы до тех, для которых подстрока является подстрокой в строке .
Поведение XPath 1.0
- преобразует набор узлов в строку , принимая строковое значение первого узла в наборе узлов .
- За
//*[contains(text(),'ABC')]
этот набор узлов будет состоять из всех дочерних текстовых узлов каждого элемента в документе. - Поскольку используется только первый дочерний текстовый узел , ожидание того, что все дочерние текстовые узлы проверяются на включение подстроки, нарушается.
- Это приводит к нелогичным результатам для тех, кто не знаком с приведенными выше правилами преобразования.
Онлайн-пример XPath 1.0 показывает, что выбран только один.
Поведение XPath 2.0+
- Это ошибка звонить
contains(string, substring)
с последовательностью из более чем одного элемента в качестве первого аргумента. - Это исправило нелогичное поведение, описанное выше в XPath 1.0.
Онлайн-пример XPath 2.0 показывает типичное сообщение об ошибке из-за ошибки преобразования, характерной для XPath 2.0+.
Общие решения
Если вы хотите включить элементы-предки, проверьте строковое значение элемента как единую строку, а не отдельные строковые значения дочерних текстовых узлов, этот XPath,
//*[contains(.,'ABC')]
выбирает ваши целевые и элементы, а также их
Addr
а такжеHome
элементы-предки, потому что они тоже имеют подстроки своих строковых значений.В онлайн-примере также показаны предки.
Если вы хотите исключить элементы-предки, этот XPath,
//*[text()[contains(.,'ABC')]]
выбирает только ваш целевой объект и поскольку только те элементы имеют дочерние текстовые узлы, строковые значения которых содержат
'ABC'
подстрока. Это будет справедливо для всех версий XPath.Онлайн-пример показывает только
Street
а такжеComment
выбирается.
[contains(text(),'')]
возвращает только true или false. Он не вернет никаких результатов элемента.
//*[text()='ABC']
возвращается
<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
Принятый ответ также вернет все родительские узлы. Чтобы получить только фактические узлы с ABC, даже если строка находится после
:
//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Вот альтернативный способ сопоставления узлов, содержащих заданную текстовую строку. Сначала запросите сам текстовый узел, затем получите родительский:
//text()[contains(., "ABC")]/..
Для меня это легко читать и понимать.
Это лучший ответ на тематический вопрос:
//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Пример:примерный случай
Xpath, чтобы получить
bon dua madam
//h3[text()='Contact Information']/parent::div/following-sibling::div/p[text()[contains(.,'bon dua madam')]]/text()[contains(.,'bon dua madam')]
Это заняло у меня немного времени, но, наконец, понял Пользовательский xpath, содержащий текст ниже, работал для меня отлично.
//a[contains(text(),'JB-')]