Search code examples
c#xdoc

Check if an xml section exist in a file using XDocument


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

Solution

  • 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.