У меня есть такие XML-документы, как:
<rootelement>
<myelement>test1</myelement>
<myelement>test2</myelement>
<myelement type='specific'>test3</myelement>
</rootelement>
Я хотел бы получить конкретный myelement
и , если его нет, , затем первый. Поэтому я пишу:
/rootelement/myelement[@type='specific' or position()=1]
The В спецификации XPath говорится о выражении 'or', которое:
Правый операнд не оценивается, если левый операнд принимает значение true
Проблема в том, что libxml2-2.6.26, похоже, применяется объединение обоих выражений, возвращающее «Набор из 2 узлов» (например, используя xmllint --shell
).
Это libxml2 или я что-то делаю не так?
Краткий ответ: ваш селектор не выражает то, что, по вашему мнению, он делает.
Оператор или
- это объединение.
Указанная вами часть спецификации («Правый операнд не оценивается ...») является частью стандартного короткого замыкания логической логики .
Вот почему вы получаете набор из двух узлов для примера ввода: XPath просматривает каждый myelement
, который является дочерним для корневого элемента
, и применяет [@ type = 'specific' или position () = 1]
для каждого такого узла, чтобы определить, соответствует ли он селектору.
test1
не соответствует @ type = 'specific'
, но соответствует position () = 1
, поэтому соответствует весь селектор. test2
не соответствует @ type = 'specific'
, а также не соответствует position () = 1
, поэтому не соответствует всему селектору. test3
соответствует @ type = 'specific'
(поэтому XPath не должен проверять его положение - это часть короткого замыкания) поэтому он соответствует всему селектору. Первый и последний
совпадают со всем селектором, поэтому он возвращает набор из двух узлов.
Самый простой способ выбрать элементы так, как вы хотите, - это сделать это в два этапа. Вот псевдокод (я не знаю, в каком контексте вы на самом деле используете XPath, и я не слишком знаком с написанием селекторов синтаксиса XPath):
элементы
, соответствующие / rootelement / myelement [@ type = 'specific']
elements
пусто, выберите элементов
, соответствующих / rootelement / myelement [position () = 1]
@Matt Ball очень хорошо объяснил причину вашей проблемы.
Вот однострочник XPath, который выбирает именно то, что вы хотите :
/*/myelement[@type='specific'] | /*[not(myelement[@type='specific'])]/myelement[1]