tags:

views:

1010

answers:

6

Lately I've been using XPathDocument and XNavigator to parse an XML file for a given XPath and attribute. It's been working very well, when I know in advance what the XPath is.

Sometimes though, the XPath will be one of several possible XPath values, and I'd like to be able to test whether or not a given XPath exists.

In case I'm getting the nomenclature wrong, here's what I'm calling an XPath - given this XML blob:

<foo>
    <bar baz="This is the value of the attribute named baz">
</foo>

I might be looking for what I'm calling an XPath of "//foo/bar" and then reading the attribute "baz" to get the value.

Example of the code that I use to do this:

XPathDocument document = new XPathDocument(filename);
XPathNavigator navigator = document.CreateNavigator();
XPathNavigator node = navigator.SelectSingleNode("//foo/bar");
if(node.HasAttributes)
{
    Console.WriteLine(node.GetAttribute("baz", string.Empty));
}

Now, if the call to navigator.SelectSingleNode fails, it will return a NullReferenceException or an XPathException. I can catch both of those and refactor the above into a test to see whether or not a given XPath returns an exception, but I was wondering whether there was a better way?

I didn't see anything obvious in the Intellisense. XPathNavigator has .HasAttributes and .HasChildren but short of iterating through the path one node at a time, I don't see anything nicer to use.

+1  A: 

If you've given valid XPath but it doesn't match anything, SelectSingleNode won't throw a NullReferenceException - it will just return null.

If you pass SelectSingleNode some syntactically invalid XPath, that's when it will throw an XPathException.

So normally, you'd just need to test whether the returned value was null or not.

Jon Skeet
That's what I was looking for, thanks.
Mark Allen
A: 

From memory, may contain errors.

XDocument doc = XDocument.Load("foo.xml");

var att = from a in doc.Descendants("bar")
          select a.Attribute("baz")

foreach (var item in att) {
    if (item != null) { ... }
}
John Sheehan
Since the question title mentions Linq, here's how I would do it.
John Sheehan
Use .FirstOrDefault on the query if you want just one
John Sheehan
A: 

If node == null then node.HasAttributes will throw a NullReferenceException. This situation will occur when //foo/bar does not match any elements in the XML document.

Justice
A: 
var node = XDocument.Load(filename)
                    .Descendants("bar")
                    .SingleOrDefault(e=>e.Attribute("baz") != null);

if (node != null) Console.WriteLine(node.Attribute("baz").Value);
Mark Cidade
A: 
 var baz = navigator.SelectSingleNode("//foo/bar/@baz");
 if (baz != null) Console.WriteLine(baz);
Mark Cidade
A: 

I would probably be more specific in my xpath.

        var doc = XDocument.Load(fileName);

        var results = from r in doc.XPathSelectElements("/foo/bar[count(@baz) > 0]")
                      select r.Attribute("baz");

        foreach (String s in results)
            Console.WriteLine(s);