0

I'm using the below code to pull out info from an XML file. But if node isn't present I get a NullReferenceException error. I thought that wasn't supposed to happen with using LINQ. But I'm very new to LINQ and XML for that matter. So I added an extention method I found from here. But I still get the error. Can someone tell me what I'm missing please?

using System;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using System.Xml.Linq;

public static class XElementExtensionMethod
{
    public static string ElementValueNull(this XElement element)
    {
        if (element != null)
        {
            return element.Value;
        }
        else
        {
            return string.Empty;
        }
    }
}

namespace XMLReader
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            string file = @"c:\users\jim\desktop\XMLData - Copy.xml";
            XElement doc = XElement.Load(file);
            StringBuilder sb = new StringBuilder();

            var nodes = from node in doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
                    select new
                    {
                        ClaimProbableAmount = (string)node.Element("ClaimsDownloadInfo").Element("ClaimsOccurrence").Element("ProbableIncurredAmt").Element("Amt").ElementValueNull()
                    };

        foreach (var node in nodes)
        {
            sb.Append("AMOUNT = ").Append(node.ClaimProbableAmount);
            MessageBox.Show(sb.ToString());
        }
    }
}
}
JimDel
  • 4,309
  • 11
  • 54
  • 99
  • I saw no reference in that "duplicate" that pertain to reading XML. – JimDel Nov 10 '14 at 19:35
  • the point of referencing that question as a duplicate is that there's only one cause of a `NullReferenceException` - a null reference, which is easily found when debugging but very difficult to find by statically looking at code. – D Stanley Nov 11 '14 at 14:34

3 Answers3

2

This is a problem:

ClaimProbableAmount = (string)node.Element("ClaimsDownloadInfo").Element("ClaimsOccurrence").Element("ProbableIncurredAmt").Element("Amt").ElementValueNull()

Since you're using Element(), you run the risk of getting the NRE. Element() returns the first instance of the named element or null. You've jumped too far down the rabbit hole.

There are a number of ways you can fix this, change all calls except the last to use Elements() instead. This time it will just return an empty collection if they don't exist. Or use other approaches such as an equivalent xpath query.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • Are you saying to change the line to ClaimProbableAmount = (string)node.Elements("ClaimsDownloadInfo").Elements("ClaimsOccurrence").Elements("ProbableIncurredAmt").Element("Amt") – JimDel Nov 10 '14 at 19:30
  • Right. That would be a start. – Jeff Mercado Nov 10 '14 at 19:32
1

The problem is almost certainly not in ElementValueNull... it's that one of the earlier Element calls is returning null. You're calling an instance method (XContainer.Element()) and that's a perfectly ordinary instance method - if it's called on a null reference, that will throw an exception just like any other instance method will.

One option to avoid this is to use Elements repeatedly instead - that way you'll end up with an empty sequence, instead of a null reference. Use FirstOrDefault at the end to get a single XElement reference or null.

Additionally:

  • There's no benefit in using an anonymous type when you've got a single property. Just select the string itself.
  • There's no benefit in using a StringBuilder when you're calling ToString() on each iteration of the loop.

I would write your query as:

var amounts = doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
                 .Select(x => (string) x.Elements("ClaimsDownloadInfo")
                                        .Elements("ClaimsOccurrence")
                                        .Elements("ProbableIncurredAmt")
                                        .Elements("Amt")
                                        .FirstOrDefault() ?? "");

(Note the lack of a need for your extension method - the explicit conversion to string will return null if the input is null, and then the null-coalescing operator will take care of using "" instead of null.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I actually am using several properties. I omitted them (incorrectly I now see) to shorten my example. Same goes for the StringBuilder. But this helped! Thanks! – JimDel Nov 10 '14 at 19:42
0

If any of the parent elements are null (i.e. ClaimsDownloadInfo, ClaimsOccurance, etc), then you'll get a NullReferenceException. The ElementValueNull extension method won't magically capture that. You'll have to get each element one at a time and confirm that each is not null:

var claimsDownloadInfo = node.Element("ClaimsDownloadInfo");
if(claimsDownloadInfo != null)
{
    var claimsOccurance = claimsDownloadInfo.Element("ClaimsOccurrence");
    if(claimsOccurance != null)
    {
        //etc
    }
}
Dave Zych
  • 21,581
  • 7
  • 51
  • 66