Solved

Reading XML data using C#.NET Linq

Posted on 2008-09-29
12
6,254 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 62

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 62

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
 

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 62

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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 62

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 62

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 62

Expert Comment

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

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
recursion example 16 69
Visual Studio: built-in keystroke automation 2 24
Image(2) 3 26
ConsoleSql 1 18
Navigation is an important part of web design from a usability perspective. But it is often a pain when it comes to a developer’s perspective. By navigation, it often means menuing. This is less theory and more practical of how to get a specific gro…
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 …
This video teaches viewers about errors in exception handling.
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

758 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now