1

I have a large number of records (about 2,000) returned as XML and I am using a LINQ statement to parse the XML and return a collection objects. I am however, getting an exception when the LINQ expressions evaluates.

object reference not set to an instance of an object

I know that somewhere the LINQ expression is trying to deal with a value that is null or missing. Is there a way that the exception or stack trace could tell me where in the XML this might be happening as I am dealing with a lot of XML.

Here is my LINQ query ...

XDocument xml = XDocument.Parse(responseXml);
List<PaymentModel> paymentDetails = xml.Descendants("CustomerPayment")
        .Select(x => new PaymentModel
        {
            PaymentRecordNumber = x.Element("InvoiceId").Value.ToString(),
            PaymentMade = (decimal)x.Element("PaymentMade"),
            PaymentDate = !string.IsNullOrEmpty(x.Element("PaymentDate").Value.ToString()) ? (DateTime)x.Element("PaymentDate") : DateTime.MinValue,
            InvoiceCollection = x.Elements("CustomerInvoices")
                .Where(
                    i => i.Element("InvoiceId").Value.Contains("USA")
                    || i.Element("InvoiceId").Value.Contains("JAPAN")
                    || i.Element("InvoiceId").Value.Contains("UK")
                    || i.Element("InvoiceId").Value.Contains("DENMARK")
                )
                .Select(i => new InvoiceModel()
                {
                    InvoiceNumber = i.Element("InvoiceNumber").Value.ToString(),
                    InvoiceAmount = (decimal)i.Element("AmountPaid")
                }).ToList< InvoiceModel>()
        }).ToList<PaymentModel>();
webworm
  • 10,587
  • 33
  • 120
  • 217

1 Answers1

1

If you just want to avoid the exception, you could use C#'s null operators to check if a value is null inline and assign a default value if needed.

You code would need to look like this:

List<PaymentModel> paymentDetails = xml.Descendants("CustomerPayment")
        .Select(x => new PaymentModel
        {
            PaymentRecordNumber = x.Element("InvoiceId")?.Value.ToString() ?? String.Empty,
            PaymentMade = Convert.ToDecimal(x.Element("PaymentMade")?.Value ?? "0"),
            PaymentDate = !string.IsNullOrEmpty(x.Element("PaymentDate")?.Value.ToString() ?? String.Empty) ? Convert.ToDateTime(x.Element("PaymentDate")?.Value): DateTime.MinValue,
            InvoiceCollection = x.Elements("CustomerInvoices")?
                .Where(
                    i => (bool)i.Element("InvoiceId")?.Value.Contains("USA")
                    || (bool)i.Element("InvoiceId")?.Value.Contains("JAPAN")
                    || (bool)i.Element("InvoiceId")?.Value.Contains("UK")
                    || (bool)i.Element("InvoiceId")?.Value.Contains("DENMARK")
                )
                .Select(i => new InvoiceModel()
                {
                    InvoiceNumber = i.Element("InvoiceNumber")?.Value.ToString() ?? String.Empty,
                    InvoiceAmount = Convert.ToDecimal(i.Element("AmountPaid")?.Value ?? "0") 
                }).ToList<InvoiceModel>()
        }).ToList<PaymentModel>();

If you want to find the errors in the XML, you could split the file to narrow down the part which causes the exception or comment out the query line by line to find at least the element's name.

Another way would be to check whether the document is valid by validating it with an XSD, which describes the XML's structure. It can help to find the location where no element or attribute exists or where unwanted elements and attributes exist. You can write the XSD by hand or even generate it from C# classes.

Sebastian Hofmann
  • 1,440
  • 6
  • 15
  • 21
  • I found out where the error was occurring (I used LINQ Pad and set the option to break when an exception is thrown. The "Parent" property showed me the XML where the trouble was. It was an `` node that was empty. So when the `WHERE` clause tried to look at the `` it found nothing there. Any idea how to bypass the whole `WHERE` clause if there is no node to parse? – webworm May 31 '18 at 18:17
  • You can check if the element has descendants with `if (x.Elements("CustomerInvoices").Descendants().Any()) {...}`, if not, just skip the `Where`. – Sebastian Hofmann May 31 '18 at 18:20