简体   繁体   中英

How to use linq2Xml without the possibility of a null exception?

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>

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

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.

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");

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.

var userData = queryUserResponseData.Elements("user")
    .Select(u => u.Element("username"))
    .Where(uNode => uNode != null)
    .Single(uName => uName.Value == userName);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM