Як вибрати перший елемент із певним атрибутом за допомогою XPath


300

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

Як я можу вибрати перший вузол, який відповідає більш складній умові, наприклад, перший вузол, який відповідає /bookstore/book[@location='US']

Відповіді:


444

Використання:

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

Спочатку отримають елементи книги з атрибутом розташування, рівним "US". Тоді він вибере перший вузол із цього набору. Зверніть увагу на використання круглих дужок, необхідних для деяких реалізацій.

Зверніть увагу, це не те саме, що, /bookstore/book[1][@location='US']якщо у першого елемента також є цей атрибут розташування.


Як я міг би зробити те саме для // книгарні / книги [@ location = 'US']?
Олександр Вікторович Ільїн

7
Це отримають усі книги з "США". (/ книгарня / книга [@ location = 'US']) [1] отримає першу.
Кевін Дрідджер

3
@KevinDriedger /bookstore/book[@location='US'][1]не повертає всі книги з "США". Я тестував це неодноразово і під різними мовами xpath реалізації. /bookstore/book[@location='US'][1]повертає першу книгу "США" під книжковим магазином. Якщо є безглузді книгарні, то вони повернуть першу з кожного. Це те, про що попросила ОП (перший вузол під книгарнею). Ваша версія повертає лише одну книгу з усіх книжкових магазинів (перша відповідність).
Джонатан Фінгланд

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.
Hlung

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. [A1, B1, C2]

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

Повертає перше місце розташування книги в США, яке існує в категорії в будь-якому місці документа. [B1]

(/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.