Solved

Reading XML data using C#.NET Linq

Posted on 2008-09-29
12
6,265 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,
0
Comment
Question by:CRCInfosys
  • 6
  • 6
12 Comments
 
LVL 63

Expert Comment

by:Fernando Soto
ID: 22599542
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
0
 

Author Comment

by:CRCInfosys
ID: 22599805
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
0
 
LVL 63

Expert Comment

by:Fernando Soto
ID: 22600594
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

0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:CRCInfosys
ID: 22600650
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,
0
 
LVL 63

Expert Comment

by:Fernando Soto
ID: 22601612
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

0
 

Author Comment

by:CRCInfosys
ID: 22625561
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.
0
 
LVL 63

Expert Comment

by:Fernando Soto
ID: 22629294
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

0
 

Author Comment

by:CRCInfosys
ID: 22652371
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.

0
 

Author Comment

by:CRCInfosys
ID: 22652493
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,
0
 
LVL 63

Accepted Solution

by:
Fernando Soto earned 500 total points
ID: 22654011
Hi CRCInfosys;

To your question, "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?", see the posted code. Basically you add another Let local variable that references that node in the documnet and then in the select clause place it in the list that will be returned and the error message always means that the object you wanted to create or get was not created or never existed.

To your question, "Also can i do that as well with the Extrinsic fields so i can set different values based on the tag? ", I rewrote the Extrinsic field format so that it returns back a structure of Extrinsic with two fields the attribute and its value see code.

To the question, "the Extrinsic name "PaymentType" and Extrinsic name "PaymentSource" in the OrderRequestHeader." to get that information you build a local variable as shown in the next line:

let reqHeaderExtrinsic = orderRequest.Element("Extrinsic")

That will get you a reference to the Extrinsic nodes in the OrderRequestHeader node. Then in the select clause you select the information as I did for the other Extrinsic node in the other part of the document.

Fernando


// To create a concreate data type for use in linq to store 
// Attribute and its value
private struct Extrinsic
{
    public String attribute;
    public String value;
}
 
private void button5_Click(object sender, EventArgs e)
{
    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")
                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")
                let shipToCity = shipToPostalAddress.Element("City")
                let shipToState = shipToPostalAddress.Element("State")
                let shipToPostalCode = shipToPostalAddress.Element("PostalCode")
                let itemDetail = itemOut.Element("ItemDetail")
                let extrinsic = itemDetail.Elements("Extrinsic")
                select new
                {
                    // The next three lines are new
                    RequestHeaderOrderID = reqHeader.Attribute("orderID").Value,
                    RequestHeaderOrderDate = reqHeader.Attribute("orderDate").Value,
                    RequestHeaderType = reqHeader.Attribute("type").Value,
                    BillToStreet = (from str in billToStreet
                                    select str.Value).ToList(),
                    BillToCity = billToCity.Value,
                    BillToState = billToState.Value,
                    BillToPostalCode = billToPostalCode.Value,
                    ShipToStreet = (from str in shipToStreet
                                    select str.Value).ToList(),
                    ShipToCity = shipToCity.Value,
                    ShipToState = shipToState.Value,
                    ShipToPostalCode = shipToPostalCode.Value,
                    Extrinsic = (from ext in extrinsic
                                 select new Extrinsic { attribute = ext.Attribute("name").Value, value = ext.Value }).ToList()
                };
 
    foreach (var orderRequest in order)  // This is where the error occurs
    {
        // The next three lines are new
        xmlTest += "Order ID = " + orderRequest.RequestHeaderOrderID + "\n";
        xmlTest += "Order Date = " + orderRequest.RequestHeaderOrderDate + "\n";
        xmlTest += "Order Type = " + orderRequest.RequestHeaderType + "\n";
        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);
 
}
 
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<Extrinsic> extrinsic)
{
    String retString = String.Empty;
    foreach (Extrinsic ext in extrinsic)
    {
        retString += ext.attribute + " = " + ext.value + "\n";
    }
    if (retString.Length > 1)
    {
        retString = retString.Substring(0, retString.Length - 2);
    }
 
    return retString;

Open in new window

0
 

Author Closing Comment

by:CRCInfosys
ID: 31501268
Thank you very much for helping with all the questions. Everything you suggested worked as expected.

Thanks,
0
 
LVL 63

Expert Comment

by:Fernando Soto
ID: 22713021
Not a problem, glad I was able to help.  ;=)
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

How to remove superseded packages in windows w60 or w61 installation media (.wim) or online system to prevent unnecessary space. w60 means Windows Vista or Windows Server 2008. w61 means Windows 7 or Windows Server 2008 R2. There are various …
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

821 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question