tags:

views:

160

answers:

5

I wrote this simple linq-to-xml query and it seems that null exception could not be avoided using the the linq syntax. Am I using it wrong? What should be the right (and short) Linq2Xml syntax?

The linq2Xml query

var userData =
    queryUserResponseData.Elements("user")
        .Single(u => u.Element("username").Value == userName);

The XML

<data>
    <user>
        <username>User1</username>
        <userid>123</userid>
    </user>
    <user>
        <username>User2</username>
        <userid>456</userid>
    </user>
    <user>
        <userid>999</userid>
    </user>
</data>
+1  A: 

Using Single means you expect there to be exactly 1 result. When more results are returned, Single will throw an exception. You could use First to get the first item, or Last for the last one. For multiple items you'll want to loop over the results and access each one individually.

If no matching result exists, you can use SingleOrDefault to return a null value or the default value of the type used.

Is queryUserResponseData an XElement or an XDocument? If it's an XDocument you need to access the XML's root first, such as:

var userData = queryUserResponseData.Root.Elements("user")
                 .Single(u => u.Element("username").Value == userName);

Apart from that, searching for User1 or User2 in your sample would work. However, if you searched for User3, which doesn't exist, Single will throw an exception. In that case you should use SingleOrDefault:

var userData = queryUserResponseData.Elements("user")
                 .SingleOrDefault(u => u.Element("username").Value == "User3");
Ahmad Mageed
I do expect for 1 result. The problem is actually at `u.Element("username").Value` where the Resharper notifies me about a possible null exception.
Eran Betzalel
ReSharper is likely being cautious and indicating that the "username" element may not exist, in which case the exception would occur. See @ChrisF's response below. @Matajon's answer is a clean way to avoid it too.
Ahmad Mageed
+5  A: 

XElement and XAttribute have some explicit conversion operators to convert their value to the specific types. They are so useful because, return null when the Element or Attribute is missing.

var userData = queryUserResponseData.Elements("user").Single(u => (string)u.Element("username") == userName);
Mehdi Golchin
+1 for `(string)u.Element("username")`. Answer would be perfect if there was some additional explanation why/how this solves the problem.
dtb
@dtb thanks for your upvote and so sorry for the lack of explanation.
Mehdi Golchin
A: 
var userData = queryUserResponseData.Elements("user")
    .Select(u => u.Element("username"))
    .Where(uNode => uNode != null)
    .Single(uName => uName.Value == userName);
Lee
+1  A: 

According to your comment to Ahmad's answer, I presume you get NullReferenceException when element doesn't have node. You can fix it like this -

var userData =
    doc.Elements("user")
    .Single(u => u.Element("username") != null && u.Element("username").Value == userName);

But if username node is required there by DTD or XSD or you are sure all elements will have username node, you can simply ignore ReSharper warning.

Matajon
+2  A: 

From your comment to Ahmad Mageed's answer:

The problem is actually at u.Element("username").Value where the Resharper notifies me about a possible null exception

it sounds like you might be worrying about a potential rather than real problem. You know that your data will mean that you will always return 1 result, however ReSharper doesn't have access to your data so it's highlighting the fact that if there were no results it would generate a null reference exception.

You can do one of three things:

  1. Ignore the warning and do nothing.

  2. Recode to account for this so that there's no chance of an exception (see the other answers).

  3. Wrap the Linq in a try {} catch {} so that if the "unthinkable" happens your program won't crash.

Only you can really decide which you want to do.

ChrisF
+1, good analysis.
Ahmad Mageed