Reputation: 11643
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page id="56">
<img id="teaser" src="img/teaser_company.png"></img>
</page>
</pages>
I have an xml file that defines additional resources for pages within a cms. What's the best way to guard for Null Reference exceptions when querying this file with LinqToXml?
var page = (from tabElement in extensionsDoc.Descendants("page")
where tabElement.Attribute("id").Value == tabId.ToString()
select tabElement).SingleOrDefault();
This code could potentially trigger a Null Reference exception if a page element doesn't have an attribute called "id". Do I have to use a try catch block or is there a way to handle this? For instance return null for page the page object if there's no attribute called "id" for the page element.
Upvotes: 12
Views: 10864
Reputation: 1500785
EDIT: This was clearly written a long time ago - these days I'd definitely go with the cast as per Igor's answer.
The simplest way would be something like:
var page = (from tabElement in extensionsDoc.Descendants("page")
let idAttribute = tabElement.Attribute("id")
where idAttribute != null
&& idAttribute.Value == tabId.ToString()
select tabElement).SingleOrDefault();
Alternatively you could write an extension method to XElement
:
public static string AttributeValueOrDefault(this XElement element,
string attributeName)
{
XAttribute attr = element.Attribute(attributeName);
return attr == null ? null : attr.Value;
}
then use:
var page = (from element in extensionsDoc.Descendants("page")
where element.AttributeValueOrDefault("id") == tabId.ToString()
select element).SingleOrDefault();
Or to use dot notation:
var page = extensionsDoc.Descendants("page")
.Where(x => x.AttributeValueOrDefault("id") == tabId.ToString())
.SingleOrDefault();
(It would make sense to call tabId.ToString()
once beforehand, btw, rather than for every iteration.)
Upvotes: 28
Reputation: 720
I would use a class entity mapping the XML element. And call a method that would check null value. I use this method in my code, it works fine. Hope it helps.
Here is a sample code to adjust depending on your needs :
private void Method1(...) {
...
var pages = from tabElement in extensionsDoc.Descendants("page")
where tabElement.Attribute("id").Value == tabId.ToString()
select new Page {
imgSrc = Method2(tabElement)
};
// pages variable is a List<Page> object
...
}
private void Method2(XElement element) {
XElement img = element.Element("img");
if (img != null) {
...
// TODO return the imgSrc
return "";
}
// return null or ""
return null;
}
Then the Page class definition :
class Page
{
public string imgSrc { get; set; }
}
Upvotes: 0
Reputation: 76510
In .NET 4 LINQ to XML provides a way to do that and it is through using explicit casts:
var page = (
from tabElement in extensionsDoc.Descendants("page")
where (string)tabElement.Attribute("id") == tabId.ToString()
select tabElement
).SingleOrDefault();
If the attribute is not there, then the result will simply be null.
In addition to explicit string
operator there are also most of the primitive types and their Nullable versions. This means that you can do AttributeOrDefault
using this kind of syntax:
//<element theAttr="12" />
int theAttr = (int?)doc.Element("element").Attribute("missingAttr") ?? 0;
Upvotes: 10
Reputation: 1560
I tend to use XPath expressions where the code would otherwise be cluttered with lots of null checks. For your example:
var query = string.Format("page[@id='{0}']", tabId.ToString());
var page = extensionsDoc.XPathSelectElement(query);
Upvotes: 1
Reputation: 9994
I've seen other people use a straight cast to a string before as well; I don't know if it's any more or less efficient than what Jon suggested, but I quite like the syntax.
var page = extensionsDoc.Descendants("page")
.Where(x => (string)x.Attribute("id") == tabId.ToString())
.SingleOrDefault();
Anyone feel free to fix that if there's some flaw in my thinking; I'm quite new to LINQ.
Upvotes: 3