3
<?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>

I have selected all books form xml file. How to select title, author( first and last name ) and price for each book with use of XPath?

xPathDoc = new XPathDocument(filePath);
xPathNavigator = xPathDoc.CreateNavigator();
XPathNodeIterator xPathIterator = xPathNavigator.Select("/bookstore//book");
foreach (XPathNavigator book in xPathIterator)
{
    ??
}
Anirudha
  • 32,393
  • 7
  • 68
  • 89
witpo
  • 465
  • 3
  • 11
  • 23

3 Answers3

11

Use SelectSingleNode() and 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);
  } 
MiMo
  • 11,793
  • 1
  • 33
  • 48
3

You can use 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 now have all the elements you want

So, you can do this now

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;
}
Anirudha
  • 32,393
  • 7
  • 68
  • 89
  • That looks great but I would like to see how I could achieve it with XPath – witpo Oct 18 '12 at 12:01
  • @witpo `xpath` is a very old technology..use something new that aids your code..not that which would make it ugly..! – Anirudha Oct 18 '12 at 12:07
  • The code needs a call to `First()` after `Descendants("author")` - and it crashes for the books that have no author. – MiMo Oct 18 '12 at 12:18
  • XPath is 'old technology' and Linq is the new and better way - but if you have a big codebase using XPath maybe it is better to stick to it for consistency - plus if you know XPath well and it works it is not necessarily justified to learn something new and different from scratch. – MiMo Oct 18 '12 at 12:20
  • @MiMo yeah..it may crash but can be avoided with null checking – Anirudha Oct 18 '12 at 12:26
  • I don't see how - `Descendants("author")` returns an enumeration of elements, there cannot be comments - and if you don't call `First()` the code does not compile: `System.Collections.Generic.IEnumerable' does not contain a definition for 'Element' and no extension method 'Element' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found` – MiMo Oct 18 '12 at 14:25
  • @MiMo will check it..`descendants` returns everything including comments and elements..`elements` method returns only elements and not comments – Anirudha Oct 18 '12 at 14:34
  • `Descendants()` returns everything, but `Descendants("author")` returns all nodes named `author` - that can be only elements, comments don't have a name – MiMo Oct 18 '12 at 17:30
0

I like the solution provided by Mimo but with a tiny change, creating an extension method to re-use part of the functionality:

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;
    }
}

The resulting code will look like cleaner:

        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);
        }
Augusto Barreto
  • 3,637
  • 4
  • 29
  • 39