Использование XPath в SelectSingleNode: извлечение отдельного элемента из XML, если он присутствует - PullRequest
13 голосов
/ 19 мая 2009

Мой XML выглядит так:

<?xml version=\"1.0\"?>
<itemSet>
       <Item>one</Item>
       <Item>two</Item>
       <Item>three</Item>
       .....maybe more Items here.
</itemSet>

Некоторые из отдельных Предмет могут присутствовать или не присутствовать. Скажем, я хочу получить элемент <Item> два </Item>, если он присутствует. Я пробовал следующие XPath (в C #).

  • XMLNode node = myXMLdoc.SelectSingleNode("/itemSet[Item='two']") --- Если присутствует элемент two , он возвращает мне только первый элемент one . Может быть, этот запрос просто указывает на первый элемент в itemSet, если у него есть Item со значением два где-то как ребенок. Правильно ли это толкование?

Итак, я попробовал:

  • XMLNode node = myXMLdoc.SelectSingleNode("/itemSet[Item='two']/Item[1]") --- Я читаю этот запрос как, верните мне первый элемент <Item> в itemSet, который имеет значение = 'two'. Я прав?

По-прежнему возвращается только первый элемент one . Что я делаю неправильно? В обоих случаях, используя братьев и сестер, я могу пройти по дочерним узлам и добраться до two , но это не то, на что я смотрю. Также, если два отсутствует, SelectSingleNode возвращает ноль. Таким образом, сам факт того, что я получаю успешный возвратный узел, действительно указывает на наличие второго элемента, поэтому если бы я хотел, чтобы булевский тест показал наличие two , то любой из приведенных выше XPath будет достаточным, но я на самом деле нужен полный элемент <Item>two</Item> в качестве моего узла возврата.

[Мой первый вопрос здесь и мой первый опыт работы с веб-программированием, поэтому я только что изучил вышеупомянутые XPath и связанные с ними XML-материалы на лету прямо из предыдущих вопросов в SO. Так что будьте нежны, и дайте мне знать, если я глупец или пренебрегаю правилами сообщества. Спасибо.]

1 Ответ

23 голосов
/ 19 мая 2009

Я думаю, что вы хотите:

myXMLdoc.SelectSingleNode("/itemSet/Item[text()='two']")

Другими словами, вы хотите, чтобы Item имел текст два, а не itemSet, содержащий его.

Вы также можете использовать одну точку для обозначения узла контекста, в вашем случае:

myXMLdoc.SelectSingleNode("/itemSet/Item[.='two']")

EDIT: Разница между . и text() заключается в том, что . означает «этот узел» эффективно, а text() означает «все дочерние узлы текстового узла этого узла». В обоих случаях сравнение будет против «строкового значения» LHS. Для узла элемента строковое значение является «объединением строковых значений всех потомков текстового узла узла элемента в порядке документа», а для набора текстовых узлов сравнение будет проверять, равен ли какой-либо текстовый узел тот, с которым ты тестируешь.

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

<root>
  <item name="first">x<foo/>y</item>
  <item name="second">xy<foo/>ab</item>
</root>

Тогда выражение XPath «root/item[.='xy']» будет соответствовать первому элементу, а «root/item[text()='xy']» будет соответствовать второму.

...