xpath 'или' ведет себя как union ('|') с xmllib2 - PullRequest
6 голосов
/ 16 августа 2010

У меня есть XML-документы, такие как:

<rootelement>
<myelement>test1</myelement>
<myelement>test2</myelement>
<myelement type='specific'>test3</myelement>
</rootelement>

Я хотел бы получить конкретные myelement и , если нет, , затем первый.Поэтому я пишу:

/rootelement/myelement[@type='specific' or position()=1]

Спецификация XPath указывает на выражение 'orоценивается как true

Проблема в том, что libxml2-2.6.26, по-видимому, применяет объединение обоих выражений, возвращая «2 узла» (например, с использованием xmllint --shell).

Это libxml2 или я что-то не так делаю?

Ответы [ 2 ]

10 голосов
/ 16 августа 2010

Краткий ответ: ваш селектор не выражает то, что вы думаете.


Оператор or равен объединению.

Частьуказанной вами спецификации («Правильный операнд не вычисляется ...») является частью стандартного короткого замыкания логической логики .

Вот почему вы получаете набор из 2 узлов для вашегоПример ввода: XPath просматривает каждый myelement, который является дочерним элементом rootelement, и применяет часть [@type='specific' or position()=1] к каждому такому узлу, чтобы определить, соответствует ли он селектору.

  1. <myelement>test1</myelement> не соответствует @type='specific', но соответствует position()=1, поэтому соответствует целому селектору.
  2. <myelement>test2</myelement> не соответствует @type='specific' и не соответствует position()=1, поэтомуон не соответствует целому селектору.
  3. <myelement type='specific'>test3</myelement> соответствует @type='specific' (поэтому XPath не нужно проверять свою позицию - это короткозамкнутая часть), поэтому он соответствует целому селектору.

Первый и последний <myelement> s соответствуют целому селектору, поэтому он возвращает набор из 2 узлов.

Самый простой способ выбрать элементы так, как вы хотите, это сделать это в два этапа.Вот псевдокод (я не знаю, в каком контексте вы на самом деле используете XPath, и я не очень знаком с написанием селекторов синтаксиса XPath):

  1. Выберите elements, которые соответствуют /rootelement/myelement[@type='specific']
  2. Если elements пусто, выберите elements, что соответствует /rootelement/myelement[position()=1]
7 голосов
/ 16 августа 2010

@ Мэтт Болл очень хорошо объяснил причину вашей проблемы.

Вот однострочный XPath, выбирающий именно то, что вы хотите :

/*/myelement[@type='specific'] | /*[not(myelement[@type='specific'])]/myelement[1] 
...