Использование XPath для разбора документа XML - PullRequest
14 голосов
/ 28 ноября 2011

Допустим, у меня есть следующий xml (быстрый пример)

<rows>
   <row>
      <name>one</name>
   </row>
   <row>
      <name>two</name>
   </row>
</rows>

Я пытаюсь разобрать это с помощью XmlDocument и XPath (в конечном итоге, чтобы я мог составить список строк).

Например ...

XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

foreach(XmlNode row in doc.SelectNodes("//row"))
{
   string rowName = row.SelectSingleNode("//name").InnerText;
}

Почему в моем цикле foreach rowName всегда "один"? Я ожидаю, что это будет «один» на первой итерации и «два» на второй.

Кажется, // имя получает первый экземпляр в документе, а не первый экземпляр в строке, как я и ожидал. В конце концов, я вызываю метод на узле "строка". Если это «как это работает», то кто-нибудь может объяснить, как я мог бы изменить его, чтобы он работал в соответствии с моими потребностями?

Спасибо

Ответы [ 7 ]

16 голосов
/ 28 ноября 2011
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

foreach(XmlNode row in doc.SelectNodes("//row"))
{
   var rowName = row.SelectSingleNode("name");
}

Правильно ли указан код, который вы разместили?Я получаю ошибку компиляции для row.SelectNode (), так как он не является членом XmlNode.

В любом случае, мой пример выше работает, но предполагает только один узел <name> в узле <row>, поэтомувам может потребоваться использовать SelectNodes() вместо SelectSingleNode(), если это не так.

Как показали другие, используйте .InnerText, чтобы получить только значение.

4 голосов
/ 28 ноября 2011

Ваш второй xpath начинается с //.Это сокращение для /descendant-or-self::node(), которое, как вы можете видеть, начинается с /, то есть оно ищет в корне документа , независимо от контекста, в котором вы его используете.

Вы, вероятно, хотите, чтобы один из:

var rowName = row.SelectSingleNode("name");

нашел name узлов, которые являются непосредственными потомками из row, или

var rowName = row.SelectSingleNode(".//name");

, чтобы найтиname узлов * где-либо под the строкой . Note the .` в этом втором xpath, который вызывает запуск xpath с узла контекста.

4 голосов
/ 28 ноября 2011

Использовать LINQ to XML. Включите using System.Xml.Linq; в ваш файл кода, а затем выполните следующий код, чтобы получить список

XDocument xDoc = XDocument.Load(filepath);
IEnumerable<XElement> xNames;

xNames = xDoc.Descendants("name");

Это даст вам список элементов имени. Тогда, если вы хотите превратить это в List<string>, просто сделайте это:

List<string> list = new List<string>();
foreach (XElement element in xNames)
{
    list.Add(element.value);
}
3 голосов
/ 28 ноября 2011

Используйте относительный путь, например string rowName = row.SelectSingleNode("name").InnerText;.

2 голосов
/ 28 ноября 2011

Я бы использовал SelectSingleNode, а затем свойство InnerText.

var rowName = row.SelectSingleNode("name").InnerText;
2 голосов
/ 28 ноября 2011

Проблема во втором запросе XPath:

//row

Это глобальная область действия, поэтому независимо от того, откуда вы ее вызываете, она выберет все row элементов.

Это должно сработать, если вы замените выражение на:

.//row
0 голосов
/ 28 ноября 2011

Используйте следующее

        doc.LoadXml(xml);

            foreach(XmlNode row in doc.SelectNodes("/rows/row"))
            {
                string rowName = row.SelectSingleNode("//name").InnerText.ToString();
            }
...