Как выбрать первый элемент с определенным атрибутом, используя XPath


300

XPath bookstore/book[1]выбирает первый узел книги под bookstore.

Как я могу выбрать первый узел, который соответствует более сложному условию, например, первый узел, который соответствует /bookstore/book[@location='US']

Ответы:


444

Использование:

(/bookstore/book[@location='US'])[1]

Сначала будут получены элементы книги с атрибутом местоположения, равным «США». Затем он выберет первый узел из этого набора. Обратите внимание на использование скобок, которые требуются в некоторых реализациях.

Обратите внимание, что это не то же самое, что /bookstore/book[1][@location='US']если первый элемент также не имеет этого атрибута местоположения.


Как я мог сделать то же самое для // bookstore / book [@ location = 'US']?
Александр Владимирович Ильин

7
Это получит все книги из «США». (/ bookstore / book [@ location = 'US']) [1] получит первый.
Кевин Дридгер

3
@KevinDriedger /bookstore/book[@location='US'][1]не возвращает все книги из США. Я проверял это много раз и в разных реализациях xpath. /bookstore/book[@location='US'][1]возвращает первую книгу «США» в книжном магазине. Если есть несколько книжных магазинов, то он вернет первый из каждого. Это то, о чем просил OP (первый узел в книжном магазине). Ваша версия возвращает только одну книгу из всех книжных магазинов (первое совпадение).
Джонатан Фингланд

3
@JonathanFingland, которую вы неправильно поняли - прочитайте ответ Кевина Дриджера снова, вместе с контекстом вопроса Александра В. Ильина. Вы оба имеете в виду одно и то же.
kiedysktos

175

/bookstore/book[@location='US'][1] работает только с простой структурой.

Добавьте немного больше структуры и вещи ломаются.

С участием

<bookstore>
 <category>
  <book location="US">A1</book>
  <book location="FIN">A2</book>
 </category>
 <category>
  <book location="FIN">B1</book>
  <book location="US">B2</book>
 </category>
</bookstore> 

/bookstore/category/book[@location='US'][1] доходность

<book location="US">A1</book>
<book location="US">B2</book>

не «первый узел, который соответствует более сложному условию». /bookstore/category/book[@location='US'][2]ничего не возвращает

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

(/bookstore/category/book[@location='US'])[1] дает

<book location="US">A1</book>

и (/bookstore/category/book[@location='US'])[2]работает как положено.


11
Автор принятого ответа здесь. Вопрос ОП рассмотрел /bookstore/book[1]и НЕ (/bookstore/book)[1]. Предоставленный вами случай отличается от того, о котором просили. Предположительно, ОП принял мой ответ, так как он сделал то, что ожидал (и попросил).
Джонатан Фингланд

Этот ответ помог мне в этом особом случае. Может кто-нибудь объяснить, почему он не справится с «более сложными ситуациями»? Поскольку в основном он находит список из двух элементов, [2] следует просто забрать его (в моем мире)
Скурпи

Я также считаю, что этот ответ является более правильным, чем выбранный ответ, поскольку в моем случае у меня также была более сложная структура, в которой простое добавление [1] возвращало несколько узлов. Спасибо!
mydoghasworms

2
Скобки работают! Вы также можете добавить больше пути после (..) [1], как: '(//div[text() = "'+ name +'"])[1]/following-sibling::*/div/text()'. В случае совпадения множества узлов name.
Хлунг

1
Я меняю свое мнение. Через некоторое время я понял, о чем говорил этот ответ, и если бы я не увидел пример ОП, я бы за это проголосовал. Я полагаю, что я реагировал на тон этого ответа; если бы @tkurki объяснил немного больше об отделении условия от выбора первого узла, я бы сразу это увидел. Возможно, то же самое для JonFingland.
Джерард ONeill

51

Как объяснение ответа Джонатана Фингланда:

  • несколько условий в одном и том же предикате ( [position()=1 and @location='US']) должны быть истинными в целом
  • несколько условий в последовательных предикатах ( [position()=1][@location='US']) должны быть истинными одно за другим
  • это подразумевает, что [position()=1][@location='US']! = [@location='US'][position()=1]
    while [position()=1 and @location='US']==[@location='US' and position()=1]
  • подсказка: одинокий [position()=1]может быть сокращен до[1]

Вы можете строить сложные выражения в предикатах с логическими операторами « and» и « or», а также с функциями Логического XPath not(), true()и false(). Кроме того, вы можете заключить подвыражения в скобки.


15

Самый простой способ найти первый английский узел книги (во всем документе), принимая во внимание более сложный структурированный XML-файл, например:

<bookstore>
 <category>
  <book location="US">A1</book>
  <book location="FIN">A2</book>
 </category>
 <category>
  <book location="FIN">B1</book>
  <book location="US">B2</book>
 </category>
</bookstore> 

такое выражение xpath:

/descendant::book[@location='US'][1]


10
    <bookstore>
     <book location="US">A1</book>
     <category>
      <book location="US">B1</book>
      <book location="FIN">B2</book>
     </category>
     <section>
      <book location="FIN">C1</book>
      <book location="US">C2</book>
     </section>
    </bookstore> 

Так, учитывая вышеизложенное; Вы можете выбрать первую книгу с

(//book[@location='US'])[1]

И это найдет первое, где есть США. [A1]

//book[@location='US']

Вернул бы узел, установленный со всеми книгами с местоположением US. [А1, В1, С2]

(//category/book[@location='US'])[1]

Вернет первое местоположение книги США, которое существует в категории в любом месте документа. [В1]

(/bookstore//book[@location='US'])[1]

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

В прямой ответ:

/bookstore/book[@location='US'][1]

Вернет вам первый узел для элемента книги с местоположением US, которое находится в книжном магазине [A1]

Кстати, если вы хотите, в этом примере найти первую книгу из США, которая не была прямым потомком книжного магазина:

(/bookstore/*//book[@location='US'])[1]

4

Используйте индекс, чтобы получить желаемый узел, если xpath сложный или имеется более одного узла с одинаковым xpath.

Пример:

(//bookstore[@location = 'US'])[index]

Вы можете дать номер, какой узел вы хотите.


2

если в данном xml предусмотрено пространство имен, лучше использовать это.

(/*[local-name() ='bookstore']/*[local-name()='book'][@location='US'])[1]


-1

С помощью онлайн- тестера xpath я пишу этот ответ ...
Для этого:

<table id="t2"><tbody>
<tr><td>123</td><td>other</td></tr>
<tr><td>foo</td><td>columns</td></tr>
<tr><td>bar</td><td>are</td></tr>
<tr><td>xyz</td><td>ignored</td></tr>
</tbody></table>

следующий xpath:

id("t2") / tbody / tr / td[1]

выходы:

123
foo
bar
xyz

Поскольку 1 означает выбор всех элементов td, которые являются первыми дочерними элементами их собственного прямого родителя.
Но следующий xpath:

(id("t2") / tbody / tr / td)[1]

выходы:

123
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.