We help IT Professionals succeed at work.

Reading XML data using C#.NET Linq

6,344 Views
Last Modified: 2012-05-05
What is the easiest way to read through an XML document to get the values of each element in C#.NET? I am trying to use Linq but when i try to loop through each record i get a "Object reference not set to an instance of an object." error.

The document is an Order Request which may include several orders and each order has different BillTo, ShipTo, Line Item etc. sections.

The basic code I am using looks like this:

String xmlFile = @"D:\TestOrder.XML";
XDocument xDoc = XDocument.Load(xmlFile, LoadOptions.SetLineInfo);
var order = from orderRequest in xDoc.Descendants("OrderRequest")
                            select new
                            {
                                BillToCity = (string)orderRequest.Element("City").Value,
                            };
                foreach (var orderRequest in order)  // This is where the error occurs
                {
                    xmlTest = orderRequest.BillToCity;
                }

Any tips would be greatly appreciated. If there is an easier/quicker way to read through an XML file than using Linq that would help as well.

Thanks,
Comment
Watch Question

Fernando SotoRetired
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Hi CRCInfosys;

The most likely cause of the error is due to the following line of code:

BillToCity = (string)orderRequest.Element("City").Value,

The reason for this is that the query returns a node that has other nodes as children in it and not all the nodes are of type "City" and therefore you get a null reference exception. Therefore you need to filter it so that it only has nodes of type "City".

If you post the XML file or a sample of it and also tell me what you need the results to look like I will be able to help more.

Fernando

Author

Commented:
FernandoSoto,

Thank you for the quick response. Sorry i forgot to attach the file i am using. I have attached it now so you can take a look. I have changed the extension to .txt so it would allow me to post it so you will have to change it to .xml.

The BilltoCity is only one node that I am trying to get. There are several SubElements that i am trying to access. That wsa just an example and i can see what your saying about the query bringing back other data as well.

Thanks,
TestOrder.txt
Fernando SotoRetired
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Hi CRCInfosys;

The following snippet should work for you.

Fernando

String xmlTest = "";
XElement xElements = XElement.Load("TestOrder.xml");
 
var order = from orderRequest in xElements.Descendants("BillTo")
            // Get the City elements of the BillTo elements
            let city = orderRequest.Descendants("City")
            // Get the first and only City Element
            where city.First().Name == "City"
            // Parse out the City name as a String
            select new { City = city.First().Value };
 
foreach (var orderRequest in order)  // This is where the error occurs
{
    xmlTest += orderRequest.City + "\n";
}
 
Console.WriteLine(xmlTest);

Open in new window

Author

Commented:
FernandoSoto,

Thanks again for the help. The code snippet given will work for simply getting the City, but is there a way to get all of the elements (such as City State Zip, Line Item Quantity, Price, Shipping Addresses) at once or does this code snippet need to be done for each element?

thanks,
Fernando SotoRetired
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Hi CRCInfosys;

This will show you how to do that. Just follow the same pattern to select other nodes.

Fernando

String xmlTest = "";
XElement xElements = XElement.Load("TestOrder.xml");
 
var order = from orderRequest in xElements.Descendants("OrderRequest")
            let reqHeader = orderRequest.Element("OrderRequestHeader")
            let billTo = reqHeader.Element("BillTo")
            let billToAddress = billTo.Element("Address")
            let billToPostalAddress = billToAddress.Element("PostalAddress")
            let billToCity = billToPostalAddress.Element("City")
            let billToState = billToPostalAddress.Element("State")
            let billToPostalCode = billToPostalAddress.Element("PostalCode")
            let itemOut = orderRequest.Element("ItemOut")
            let shipTo = itemOut.Element("ShipTo")
            let shipToAddress = shipTo.Element("Address")
            let shipToPostalAddress = shipToAddress.Element("PostalAddress")
            let shipToCity = shipToPostalAddress.Element("City")
            let shipToState = shipToPostalAddress.Element("State")
            let shipToPostalCode = shipToPostalAddress.Element("PostalCode")
            select new
            {
                BillToCity = billToCity.Value,
                BillToState = billToState.Value,
                BillToPostalCode = billToPostalCode.Value,
                ShipToCity = shipToCity.Value,
                ShipToState = shipToState.Value,
                ShipToPostalCode = shipToPostalCode.Value
            };
 
foreach (var orderRequest in order)  // This is where the error occurs
{
    xmlTest += orderRequest.BillToCity + " - " + orderRequest.BillToState + " - " + orderRequest.BillToPostalCode + "\n";
    xmlTest += orderRequest.ShipToCity + " - " + orderRequest.ShipToState + " - " + orderRequest.ShipToPostalCode + "\n";
}
 
Console.WriteLine(xmlTest);

Open in new window

Author

Commented:
Thank you Fernando, what you posted works out perfectly. A couple more questions, In the XML document I attached there are several Elements with the same tag such as the BillToor ShipTo Street fields. There are two of the fields there. Or in the ItemOut section there are several "Extrinsic Name" fields that are identified by different names such as "quantityMultiplier", "costCenter", "ProductSpecs" etc. How do i distinguish between them in my code?

Thanks again for all the help as everything you have given me is exactly what im looking for.
Fernando SotoRetired
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Hi CRCInfosys;

Here is how to handle multiple items with the same name. The snippet uses a query within a query or just sub-query.

Fernando

String xmlTest = "";
XElement xElements = XElement.Load("TestOrder.xml");
 
var order = from orderRequest in xElements.Descendants("OrderRequest")
            let reqHeader = orderRequest.Element("OrderRequestHeader")
            let billTo = reqHeader.Element("BillTo")
            let billToAddress = billTo.Element("Address")
            let billToPostalAddress = billToAddress.Element("PostalAddress")
            let billToCity = billToPostalAddress.Element("City")
            let billToStreet = billToPostalAddress.Elements("Street")					//New Line
            let billToState = billToPostalAddress.Element("State")
            let billToPostalCode = billToPostalAddress.Element("PostalCode")
            let itemOut = orderRequest.Element("ItemOut")
            let shipTo = itemOut.Element("ShipTo")
            let shipToAddress = shipTo.Element("Address")
            let shipToPostalAddress = shipToAddress.Element("PostalAddress")
            let shipToStreet = shipToPostalAddress.Elements("Street")					//New Line
            let shipToCity = shipToPostalAddress.Element("City")
            let shipToState = shipToPostalAddress.Element("State")
            let shipToPostalCode = shipToPostalAddress.Element("PostalCode")
            let itemDetail = itemOut.Element("ItemDetail")										//New Line
            let extrinsic = itemDetail.Elements("Extrinsic")									//New Line
            select new
            {
                BillToStreet = (from str in billToStreet							  			//New Line
                                select str.Value).ToList(),
                BillToCity = billToCity.Value,
                BillToState = billToState.Value,
                BillToPostalCode = billToPostalCode.Value,
                ShipToStreet = (from str in shipToStreet											//New Line
                                select str.Value).ToList(),
                ShipToCity = shipToCity.Value,
                ShipToState = shipToState.Value,
                ShipToPostalCode = shipToPostalCode.Value,
                Extrinsic = (from ext in extrinsic				  									//New Line
                             select (ext.Attribute("name").Value + " : " + ext.Value)).ToList()
            };
 
foreach (var orderRequest in order)  // This is where the error occurs
{
    xmlTest += GetStreetParts(orderRequest.BillToStreet) + " - " + 
                orderRequest.BillToCity + " - " + orderRequest.BillToState + " - " + 
                orderRequest.BillToPostalCode + "\n\n";
    xmlTest += GetStreetParts(orderRequest.ShipToStreet) + " - " + 
                orderRequest.ShipToCity + " - " + orderRequest.ShipToState + " - " + 
                orderRequest.ShipToPostalCode + "\n\n";
    xmlTest += GetExtrinsicParts(orderRequest.Extrinsic);
    xmlTest += "\n===============================================\n";
 
}
 
Console.WriteLine(xmlTest);
 
// New code to handle the multiple line returns in the new items
private String GetStreetParts(List<String> street)
{
    String retString = String.Empty;
    foreach (String str in street)
    {
        retString += str + "; ";
    }
    if (retString.Length > 1)
    {
        retString = retString.Substring(0, retString.Length - 2);
    }
 
    return retString;
}
 
private String GetExtrinsicParts(List<String> extrinsic)
{
    String retString = String.Empty;
    foreach (String ext in extrinsic)
    {
        retString += ext + "\n";
    }
    if (retString.Length > 1)
    {
        retString = retString.Substring(0, retString.Length - 2);
    }
 
    return retString;
}

Open in new window

Author

Commented:
Fernando,

Thanks again as that is helping out very much. A couple more questions, I am not able to get the value of the "orderType" attribute from the OrderRequestHeader in the xml document. When i try to return the value i get the same error as before (Object reference not set to an instance of an object). Do you know what that would be or how i can get that value? Also when i originally wrote the test code for you to look at i simply had one variable xmlTest. In fact, i have a different variable for each value. With that in mind can you return an array or list of data using LINQ so i can get individual values? such as: strStreet1 = BillToStreet1.Value,
strStreet2 = BillToStreet2.Value,

Also can i do that as well with the Extrinsic fields so i can set different values based on the tag? For example setting different variables equal to the Extrinsic name "PaymentType" and Extrinsic name "PaymentSource" in the OrderRequestHeader. And going off of that, how would i access the "PaymentType" and "Amount" values from the "PaymentSource" section?

Thank you so much for all your help.

Author

Commented:
Please disregard the question about the array concerning the Streets as that is simple and i have that already sorry. However i still am not sure how to get the array or list of values for the extrinsic values.

Thanks,
Retired
CERTIFIED EXPERT
Distinguished Expert 2017
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Author

Commented:
Thank you very much for helping with all the questions. Everything you suggested worked as expected.

Thanks,
Fernando SotoRetired
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Not a problem, glad I was able to help.  ;=)

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.