Loser Coder
Loser Coder

Reputation: 2428

Avoid object null reference exception when accessing sub-elements that may or may not exist

I have: An XML with some elements. A sub-element that may or may not be defined inside this XML. Need to extract the value of the sub-element when it does exist.

How do I get the value without throwing object-reference errors?

For example:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

//Pass in <Tag2> and the code works: 
//string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
 XDocument sampleDoc = XDocument.Parse(sampleXML);

//Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
XElement sampleEl = sampleDoc.Root; 
string tag1 = String.IsNullOrEmpty(sampleEl.Element("Tag1").Value) ? "" : sampleEl.Element("Tag1").Value;

//NullReferenceException:
//Object reference not set to an instance of an object.
string tag2 = String.IsNullOrEmpty(sampleEl.Element("Tag2").Value) ? "" : sampleEl.Element("Tag2").Value;

Upvotes: 3

Views: 10044

Answers (8)

Mr.B
Mr.B

Reputation: 3787

C# 6.0 allows us to make the expression shorter and simpler:

 string uniqueIdentifier = myNode.Document?.Element("elem1")?.Element("elem2")?.Attribute("attribute1")?.Value;

Upvotes: 1

Heinzi
Heinzi

Reputation: 172310

Just for the fun of it, here's a solution using LINQ to XML which

  • requires only one statement and
  • does not need the ternary operator, so you don't need to specify the name of the tag twice:

    string tag1 = sampleEl.Elements("Tag1").Select(x => x.Value).FirstOrDefault();
    string tag2 = sampleEl.Elements("Tag2").Select(x => x.Value).FirstOrDefault();
    

Add ?? "" if you want the empty string instead of null if the tag does not exist.

Upvotes: 0

JonH
JonH

Reputation: 33153

First you should check if the document is null, remember you are accessing the .Value and this will throw a null reference exception so before you apply .value do a test:

if (sampleEl != null)
  //now apply .value

Or ternary:

string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty

Your code then becomes:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

    //Pass in <Tag2> and the code works: 
    //string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
     XDocument sampleDoc = XDocument.Parse(sampleXML);

    //Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
    XElement sampleEl = sampleDoc.Root; 
    string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : String.Empty;

    //NullReferenceException:
    //Object reference not set to an instance of an object.
string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty

Upvotes: 2

BrokenGlass
BrokenGlass

Reputation: 160922

You can use the null-coalescing-operator for a shortcut:

 string tag1= (string)sampleEl.Element("Tag1") ?? string.Empty;

This also uses the fact that LINQ to XML allows the cast operation to get the value of the element (in this case cast to string), but returns null if the element does not exist.

Upvotes: 10

KeithS
KeithS

Reputation: 71573

I came up with this extension method. It requires you to specify the property to access as a lambda, and a default value to use if the actual value or anything up the chain is null:

public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection, TOut defaultValue)
        where TOut : class
    {
        try
        {
            return projection(input) ?? defaultValue;
        }
        catch (NullReferenceException)
        {
            return defaultValue;
        }
        catch (InvalidOperationException)
        {
            return defaultValue;
        }
    }

Usage:

var value = topObject.ValueOrDefault(x=>x.ChildObject.AnotherChild.ChildProperty, String.Empty);

value will be the empty string if topObject, ChildObject, AnotherChild, or ChildProperty are null. If all of those are valid references, the return will be whatever ChildProperty actually is (which could still be the empty string). The catch for the NullReferenceException handles references of child members of a null reference. For nullable types, InvalidOperationException is thrown when accessing the Value property of a null nullable type.

Upvotes: 1

mgronber
mgronber

Reputation: 3419

string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : string.Empty;

Upvotes: 1

andypaxo
andypaxo

Reputation: 6641

The C# ternary operator is pretty good for this:

string tag2 = sampleEl.Element("Tag2") == null ? "" : sampleEl.Element("Tag2").Value;

Upvotes: 3

JaredPar
JaredPar

Reputation: 754913

You'll need to check for null in order to prevent them. Given the repeated pattern you're using I would just factor this off into an extension method.

public static string GetElementValue(this XElement parent, string elementName) {
  if (parent == null) { 
    return string.Empty;
  }
  var element = parent.Element(elementName);
  if (element == null || element.Value == null) {
    return string.Empty;
  }
  return element.Value;
}

Now your above code can be replaced with the following

string tag1 = sampleEl.GetElementValue("Tag1");
string tag2 = sampleEl.GetElementValue("Tag2");

Upvotes: 3

Related Questions