ARTICLE

LINQ to XML in a Nutshell

Posted by Dhananjay Kumar Articles | XML November 01, 2011
Data may exist in many forms.  You may have data in relational tables residing on a relational database or portable data in the form of XML.  Since data exists in many forms, obviously there are many ways to manipulate or access it.  One of the most popular ways of sharing data is as XML.
Reader Level:

Data may exist in many forms.  You may have data in relational tables residing on a relational database or portable data in the form of XML.  Since data exists in many forms, obviously there are many ways to manipulate or access it.  One of the most popular ways of sharing data is as XML. 

Considering the prominent presence of XML as way of data sharing, LINQ to XML got introduced in C# 3.0 to work effectively and efficiently with XML data. The LINQ to XML API contains classes to work with XML. All classes of LINQ to XML are in the namespace System.XML.Linq.

fig1.gif

The objective of this article is to understand, how could we work with LINQ to XML?

Let us start with the below image. It depicts an isomorphic relationship between XML elements and corresponding LINQ to XML classes. 

fig2.gif

An XML Element is a fundamental XML construct. An Element has a name and optional attributes. An XML Element can have nested Elements called Nodes also. 

XML Element is represented by the XElementclass in LINQ to XML. It is defined in namespace System.Xml.Linq. And it inherits the class XContainerthat derives from XNode. Below tasks can be performed using XElement class

a.       It can add a child element.

b.      It can delete a child element.

c.       It can change a child element.

d.    It can add attributes to an element.

e.     It can be used to create an XML tree

f.        It is used to serialize the content into a text form

XElementclass got many overloaded constructors. You can pass XAttribute to create XML elements with attribute. 

If you examine below code snippet, I am creating XML with root element Root and many child elements. Child Element Data1 and Data2 got attributes name and ID respectively with value Dj and U18949

Code Listing 1

XElementxmltree = newXElement("Root",
newXElement("Data1", newXAttribute("name", "Dj"), 1),
new
XElement("Data2", newXAttribute("ID", "U18949"),
new
XAttribute("DEPT","MIT"),2),
newXElement("Data3", "3"),
new
XElement("Data4", "4")
 );
Console.WriteLine(xmltree);

On executing the above code you should get the below output,

fig3.gif

Let us stop here and examine how the Attributes of XML are mapped in XAttribute of LINQ to XML. XML Attribute is a Name/Value pair associated with XML elements. XAttribute class represents XML Attributes in LINQ to XML. XAttribute class is overloaded with two constructors. Most frequent used constructor is one takes name and values as input parameter.  In CodeListing1 ,attributes  to element is being created using XAttribute class.

A XML tree can be constructed using XAttribute and XElement class.

Assume you have a list of Authors as below code listing, Author is a custom class.

Code Listing 2

staticList<Author> CreateAuthorList()
        {
List<Author> list = newList<Author>()
                                 {
newAuthor(){Name="Dhananjay Kumar",NumberofArticles= 60},
newAuthor (){Name =" Pinal Dave ", NumberofArticles =5},
newAuthor () {Name = " Deepti maya patra",NumberofArticles =55},
newAuthor (){Name=" Mahesh Chand",NumberofArticles = 700},
newAuthor (){Name =" Mike Gold",NumberofArticles = 300},
newAuthor(){Name ="John Papa",NumberofArticles = 200},
newAuthor (){Name ="Shiv Prasad Koirala",NumberofArticles=100},
newAuthor (){Name =" Tim  ",NumberofArticles =50},
newAuthor (){Name=" J LibertyNumberofArticles =50}
                                 };
return list; 
        }
class
Author
        {
publicstring Name { get; set; }
publicint NumberofArticles { get; set; }
        }

An XML tree can be constructed from List of Authors as below code listing,

Code Listing 3

List<Author> list = CreateAuthorList();
XElement xmlfromlist = newXElement("Authors",
from a in list
select
newXElement("Author",
newXElement("Name", a.Name),
newXElement("NumberOfArticles", a.NumberofArticles)));
 xmlfromlist.Save(@"e:\\a.xml");

The above code snippet will create Authors as the root element. There may be any number of Authors as child element inside root element Authors. There are two other elements Name and NumberOfArticles are in XML tree.

There may be a scenario when you want to create XML tree from a SQL Server table.  You need to follow below steps,

  1.    Create Data Context class using LINQ to SQL class
  2.    Retrieve  data to parse as XML
  3.    Create XML tree
  4.    Create elements and attributes using XElement and XAttribute
  5.    WCF is name of table.

Code Listing 4

DataClasses1DataContext context = newDataClasses1DataContext();
var res = from r in context.WCFs select r;
XElement
xmlfromdb = newXElement("Employee",
from a in res
select
newXElement("EMP",
newXElement("EmpId", a.EmpId),
newXElement("Name", a.Name)));
xmlfromlist.Save(@"e:\\a.xml");

By this point you know various ways of constructing XML tree and saving on file system. Now next thing come to your mind would be how to parse XML files using LINQ.

Parsing of XML document means reading XML document, identifies the function of each of the document and then makes this information available in memory for rest of the program. XElement.Parse () methodis used to parse XML.  This is an overloaded method. This takes a string input parameter to parse. Second overloaded method takes extra input parameter LoadOptions. LoadOption defines where to preserve space in information or not?

Below code snippet is parsing a string with space preserve. 

Code Listing 5

XElementxmltree = XElement.Parse(@"<Address><Name>Dhananjay Kumar </Name><Road> Padma Road </Road></Address>",LoadOptions.PreserveWhitespace);

Console.WriteLine(xmltree);

After knowing all the pieces of LINQ to XML, let us go ahead and find how we could put all information we have so far to bind information from XML file to DataGrid of Silverlight. It is a common requirement when you need to bind or display data from XML File to Silverlight Data Grid.

Essentially there are three steps you need to execute to bind XML to Silverlight Data Grid.

  1. Download content of XML file as string using WebClient class.
  2. Parse  XML file using LINQ to XML
  3. Bind parsed result as item source of Data Grid.

Very first you need to prepare XML file as data source. Put XML file in bin folder of Silverlight project. However you can parse XML file from remote location as well.

We are going to bind Data.xml residing in client bin folder to Data Grid.

Code Listing 6

<?xml version="1.0" encoding="utf-8" ?>
<School>
  <Student RollNumber="1" Name="John Papa" />
  <Student RollNumber="2" Name="Scott Gui" />
  <Student RollNumber="3" Name="Jessy Liberty" />
  <Student RollNumber="4" Name="Tim Huer" />
  <Student RollNumber="5" Name="Victor G" />
  <Student RollNumber="6" Name="Mahesh Chand" />
  <Student RollNumber="7" Name="Pinal Dave" />
  <Student RollNumber="8" Name="Suprotim Agarwal" />
  <Student RollNumber="9" Name="Dhananjay Kumar" />
  <Student RollNumber="10" Name="Kunal Chawudhary" />
  <Student RollNumber="11" Name="Abhijit Jana" />
  <Student RollNumber="12" Name="Shiv Prasad Koirala" />
</School
>

Next task you need to design XAML page.  I am keeping it simple and putting a Button and DataGrid . On click event of button datagrid will be bind with data from xml file.

Xaml design would look like below code snippet.

Code Listing 7

<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             x:Class="SilverlightApplication5.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">     <Grid x:Name="LayoutRoot" Background="White">         <StackPanel Orientation="Vertical" Margin="50,50,50,50">         <Button x:Name="btnDemo" Content="Click To get Data From  XML File" Height="62" Width="362" />        <sdk:DataGrid x:Name="grdXmlData" Height="Auto" Width="Auto" AutoGenerateColumns="True" />         </StackPanel>              </Grid> </UserControl>

On click event of button, make an asynchronous call and download the file.

Code Listing 8

private void btnDemo_Click(object sender, RoutedEventArgs e)
{
      WebClient client = new WebClient();
      Uri uritoXML = new Uri("Data.xml"UriKind.Relative);
      client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
      client.DownloadStringAsync(uritoXML);
}

Once string is downloaded, you need to parse the downloaded XML.

Code Listing 9

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{

      if (e.Error != null)
      {
           MessageBox.Show("There is Error Downloading Data from XML File ");
      }
      else
      {

           ParseXMLFile(e.Result);
      }
}

XML can be parsed as below.  On examining below code, you will find XML file is loaded using XDocument. After loading file is being traversed using Descendants method. On getting the values for each Studentnode, you need to create instance of Student and add it to the list of Student.

Code Listing 10

void ParseXMLFile(string  dataInXmlFile)
{

      lstStudents = new List<Student>();

      XDocument xmlDoc = XDocument.Parse(dataInXmlFile);
      lstStudents  = (from r in xmlDoc.Descendants("Student")
      select new Student
      {
             Name = (string) r.Attribute("Name").Value,
             RollNumber =(string) r.Attribute("RollNumber").Value 
      }).ToList();

      grdXmlData.ItemsSource = lstStudents; 
}

Explanation

  • Function is taking string as input parameter.  Here we will pass e.Result from Downloadcompletedstring event.
  • Creating an instance of XDocument  by parsing string
  • Reading each descendants or element on Xml file and assigning value of each attribute to properties of Entity class (Student).

We need to create an Entity class to map the data from XML File. I am going to create a class Student with properties exactly as the same of attributes of Student Element in XML file.

Student class is listed as below,

Code Listing 11

public class Student
{
      public string RollNumber { getset; }
      public string Name { getset; }
}
 
On running you should get the expected output.

Assume you have data in XML file as below.  This file is save in location d drive.

Data.Xml

<?xml version="1.0" encoding="utf-8" ?>
<catalog>
  <books>
<book id="bk101"
  <author id="1">Gambardella, Matthew</author>
  <title>XML Developer's Guide</title>
  <genre>Computer</genre>
  <price>44.95</price>
  <publish_date>2000-10-01</publish_date>
   <description> An in-depth look at creating applications with XML.</description>
</book>
<book id="bk102">
  <author id="2">Ralls, Kim</author>
  <title>Midnight Rain</title>
  <genre>Fantasy</genre>
  <price>5.95</price>
  <publish_date>2000-12-16</publish_date>
  <description> A former architect battles corporate zombies,an evil sorceress, and her own childhood to become queen of the world.</description>
</book>
<book id="bk103">
  <author id="3">Corets, Eva</author>
  <title>Maeve Ascendant</title>
  <genre>Fantasy</genre>
    <price>5.95</price>
    <publish_date>2000-11-17</publish_date>
    <description>After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society. </description>
  </book>

<book id="bk104">
    <author id="4">Corets, Eva</author>
    <title>Oberon's Legacy</title>
    <genre>Fantasy</genre>
    <price>5.95</price>
    <publish_date>2001-03-10</publish_date>
    <description>In post-apocalypse England, the mysterious agent known only as Oberon helps to create a new life for the inhabitants of London. Sequel to Maeve Ascendant.</description>

  </book>

<book id="bk105">
    <author id="5">Corets, Eva</author>
    <title>The Sundered Grail</title>
    <genre>Fantasy</genre>
    <price>5.95</price>
    <publish_date>2001-09-10</publish_date>
    <description>The two daughters of Maeve, half-sisters, battle one another for control of England. Sequel to Oberon's Legacy.</description>

  </book>
  </books>
</catalog
>

To fetch all the Books, just you need to parse the XML file. Find the descendants book and fetch it in anonymous class.

XDocument document = XDocument.Load("D:\\Data.xml");
#region Fetch All the Books 
var books = from r in document.Descendants("book")
select new
{
      Author = r.Element("author").Value,
      Title = r.Element("title").Value,
      Genere = r.Element("genre").Value,
      Price = r.Element("price").Value,
      PublishDate = r.Element("publish_date").Value,
      Description = r.Element("description").Value,
};

foreach (var r in books)
{
      Console.WriteLine(r.PublishDate + r.Title + r.Author);
}

If you want to fetch a particular book, you need to apply where condition while parsing XML file.

var selectedBook = from r in document.Descendants("book").Where
                                   (r=>(string)r.Attribute("id")=="bk102")
select new
{
       Author = r.Element("author").Value,
       Title = r.Element("title").Value,
       Genere = r.Element("genre").Value,
       Price = r.Element("price").Value,
       PublishDate = r.Element("publish_date").Value,
       Description = r.Element("description").Value,
};
foreach (var r in selectedBook)
{
       Console.WriteLine(r.PublishDate + r.Title + r.Author);
}

You need to fetch author Id of a particular book with Id bk102. To do that you need to select as below,

var selectedBookAttribute = (from r in document.Descendants("book").Where

(r => (string)r.Attribute("id") == "bk102"select r.Element("author").Attribute("id").Value).FirstOrDefault();
            
Console.WriteLine(selectedBookAttribute);         

To fetch the entire author name, you need to execute below query.

var allauthors = from r in document.Descendants("book")
                                         select r.Element("author"
).Value;
foreach(var r in allauthors)
{
     Console.WriteLine(r.ToString());
}

I hope this article was useful. Thanks for reading.

COMMENT USING

Trending up