XPathNodeIterator доступ к дочерним узлам из повторяемых данных

<?xml version="1.0"?>

-<bookstore>            
        <book > 
            <title>aaaa</title> 
            -<author > 
                <first-name>firts</first-name> 
                <last-name>last</last-name> 
            </author> 
            <price>8.23</price> 
            <otherbooks>
                    <book > 
                        <title>bbb</title>      
                        <price>18.23</price> 
                    </book>     
                    <book > 
                        <title>ccc</title>      
                        <price>11.22</price> 
                    </book>     
            </otherbooks>
        </book> 
</bookstore>

Я выбрал все книги из xml файла. Как выбрать название, автора (имя и фамилию) и цену для каждой книги с использованием XPath?

xPathDoc = new XPathDocument(filePath);
xPathNavigator = xPathDoc.CreateNavigator();
XPathNodeIterator xPathIterator = xPathNavigator.Select("/bookstore//book");
foreach (XPathNavigator book in xPathIterator)
{
    ??
}

person witpo    schedule 18.10.2012    source источник
comment
Вы уже используете XPath в своем коде (.Select("/bookstore//book")). Вы пробовали использовать его снова?   -  person Tomalak    schedule 18.10.2012
comment
Я пробовал, но у меня ничего не получалось.   -  person witpo    schedule 18.10.2012
comment
@witpo: см. мой ответ о том, как это сделать с помощью XPaths   -  person MiMo    schedule 18.10.2012


Ответы (3)


Используйте SelectSingleNode() и Value:

  XPathDocument xPathDoc = new XPathDocument(filePath); 
  XPathNavigator xPathNavigator = xPathDoc.CreateNavigator(); 
  XPathNodeIterator xPathIterator = xPathNavigator.Select("/bookstore//book"); 
  foreach (XPathNavigator book in xPathIterator) 
  {
    XPathNavigator nav = book.SelectSingleNode("title");
    string title = nav==null ? string.Empty : nav.Value;
    nav = book.SelectSingleNode("author/first-name");
    string authorFirstName = nav==null ? string.Empty : nav.Value;
    nav = book.SelectSingleNode("author/last-name");
    string authorLastName = nav==null ? string.Empty : nav.Value;
    nav = book.SelectSingleNode("price");
    string price = nav==null ? string.Empty : nav.Value;;
    Console.WriteLine("{0} {1} {2} {3}", title, authorFirstName, authorLastName, price);
  } 
person MiMo    schedule 18.10.2012

Вы можете использовать LINQ2XML

XElement doc=XElement.Load("yourXML.xml");//loads your xml
var bookList=doc.Descendants().Elements("book").Select(
x=>//your book node
    new{
           title=x.Element("title").Value,
           author=new //accessing your author node
           {
               firstName=x.Element("author").Element("first-name").Value,
               lastName=x.Element("author").Element("last-name").Value
           },
           price=x.Element("price").Value
       }
);

bookList теперь есть все элементы, которые вам нужны

Итак, вы можете сделать это сейчас

foreach(var book in bookList)
{
book.title;//contains title of the book
book.author.firstName;//contains firstname of that book's author
book.author.lastName;
}
person Anirudha    schedule 18.10.2012
comment
Выглядит отлично, но я хотел бы посмотреть, как я могу этого добиться с помощью XPath. - person witpo; 18.10.2012
comment
@witpo xpath - это очень старая технология .. используйте что-то новое, что поможет вашему коду .. не то, что сделало бы его уродливым ..! - person Anirudha; 18.10.2012
comment
Коду нужен вызов First() после Descendants("author") - и он вылетает для книг, у которых нет автора. - person MiMo; 18.10.2012
comment
XPath - это `` старая технология '', а Linq - новый и лучший способ, но если у вас есть большая кодовая база с использованием XPath, возможно, лучше придерживаться его для согласованности - плюс, если вы хорошо знаете XPath и он работает, это не обязательно оправдано узнать что-то новое и необычное с нуля. - person MiMo; 18.10.2012
comment
@MiMo да .. это может привести к сбою, но его можно избежать с помощью нулевой проверки - person Anirudha; 18.10.2012
comment
Я действительно ценю твою помощь. Спасибо - person witpo; 18.10.2012
comment
Я не понимаю, как - Descendants("author") возвращает перечисление элементов, комментариев быть не может - и если вы не вызываете First(), код не компилируется: System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>' does not contain a definition for 'Element' and no extension method 'Element' accepting a first argument of type 'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>' could be found - person MiMo; 18.10.2012
comment
@MiMo проверит это .._ 1_ возвращает все, включая комментарии и элементы .._ 2_ метод возвращает только элементы, а не комментарии - person Anirudha; 18.10.2012
comment
Descendants() возвращает все, но Descendants("author") возвращает все узлы с именем author - это могут быть только элементы, комментарии не имеют имени - person MiMo; 18.10.2012

Мне нравится решение, предоставленное Mimo, но с небольшим изменением, создающим метод расширения для повторного использования части функциональности:

public static class XPathNavigatorExtensions
{
    public static string GetChildNodeValue(this XPathNavigator navigator, string nodePath)
    {
        XPathNavigator nav = navigator.SelectSingleNode(nodePath);
        return nav == null ? string.Empty : nav.Value;
    }
}

Полученный код будет выглядеть более чистым:

        ICollection<Book> books = new List<Book>();
        foreach (XPathNavigator node in iterator)
        {
            Book book = new Book() { Author = new Author() };
            book.Title = node.GetChildNodeValue("title");
            book.Author.FirstName = node.GetChildNodeValue("author/first-name");
            book.Author.LastName = node.GetChildNodeValue("author/last-name");
            book.Price = node.GetChildNodeValue("price");
            books.Add(book);
        }
person Augusto Barreto    schedule 05.03.2015