0

I have some code that reads in an xml file. However it is triggering an error at the 3rd IF statement:

if (xdoc.Root.Descendants("HOST").Descendants("Default")
    .FirstOrDefault().Descendants("HostID")
    .FirstOrDefault().Descendants("Deployment").Any())

Error:

System.NullReferenceException: Object reference not set to an instance of an object.

That is because in this particular file there is no [HOST] section.

I was assuming that on the first IF statement, if it didn't find any [HOST]section it would not go into the statement and therefore i should not get this error. Is there a way to check if a section exists first?

XDocument xdoc = XDocument.Load(myXmlFile);

if (xdoc.Root.Descendants("HOST").Any())
{
    if (xdoc.Root.Descendants("HOST").Descendants("Default").Any())
    {
        if (xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").FirstOrDefault().Descendants("Deployment").Any())
        {
            if (xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").Any())
            {
                var hopsTempplateDeployment = xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").FirstOrDefault().Descendants("Deployment").FirstOrDefault();
                deploymentKind = hopsTempplateDeployment.Attribute("DeploymentKind");
                host = hopsTempplateDeployment.Attribute("HostName");
            }
        }
    }
}
Milo
  • 3,365
  • 9
  • 30
  • 44
John
  • 787
  • 4
  • 11
  • 28
  • Sometimes instead of Any() you need to use Count() > 0. Any gives an error if you have a null object. – jdweng Oct 23 '19 at 12:31
  • Thanks, i'll try that. – John Oct 23 '19 at 12:41
  • @jdweng: Never use `Count() > 0` in place of `Any()`. On long list you will get a measurable perofrmance impact. And it won't solve the problem either. – Sefe Oct 23 '19 at 13:10

1 Answers1

1

Within the body of this if block...

if (xdoc.Root.Descendants("HOST").Descendants("Default").Any())
{
    if (xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").FirstOrDefault().Descendants("Deployment").Any())
    {
        if (xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").Any())
        {
            var hopsTempplateDeployment = xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault().Descendants("HostID").FirstOrDefault().Descendants("Deployment").FirstOrDefault();
            deploymentKind = hopsTempplateDeployment.Attribute("DeploymentKind");
            host = hopsTempplateDeployment.Attribute("HostName");
        }
    }
}

...you have established that the element <Root>/HOST/Default exists. You however don't know whether <Root>/HOST/Default/HostId/Deployment exists. If it doesn't you will get a NullReferenceException like the one you're experiencing due to the use of FirstOrDefault. It is generally recommended to use First in cases where you expect the elements to be present, which will give you at least a better error message.

If you expect the elements to be not present, a simple solution is to use the ?. along the respective LINQ2XML axis:

var hopsTemplateDeployment =
    xdoc.Root.Descendants("HOST").Descendants("Default").FirstOrDefault()
    ?.Descendants("HostID").FirstOrDefault()
    ?.Descendants("Deployment").FirstOrDefault();
if (hopsTemplateDeployment != null)
{
    deploymentKind = hopsTemplateDeployment.Attribute("DeploymentKind");
    host = hopsTemplateDeployment.Attribute("HostName");
}

It will also save you the chain of nested if clauses.

Sefe
  • 13,731
  • 5
  • 42
  • 55